UNPKG

caravan-x

Version:

A terminal-based utility for managing Caravan multisig wallets in regtest mode. This tool simplifies development and testing with Caravan by providing an easy-to-use interface

444 lines (443 loc) • 19.8 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.SettingsCommands = void 0; const prompts_1 = require("@inquirer/prompts"); const chalk_1 = __importDefault(require("chalk")); const boxen_1 = __importDefault(require("boxen")); const fs = __importStar(require("fs-extra")); const path = __importStar(require("path")); const profiles_1 = require("../core/profiles"); const setupWizard_1 = require("../ui/setupWizard"); const config_1 = require("../types/config"); class SettingsCommands { constructor(appDir) { this.appDir = appDir; this.configPath = path.join(appDir, "config.json"); this.profileManager = new profiles_1.ProfileManager(appDir); } async showSettingsMenu() { let continueMenu = true; while (continueMenu) { console.clear(); console.log(chalk_1.default.cyan.bold("\nāš™ļø Settings\n")); console.log(chalk_1.default.gray("━".repeat(50)) + "\n"); const action = await (0, prompts_1.select)({ message: "What would you like to do?", choices: [ { name: chalk_1.default.cyan("šŸ“‹ View All Configurations"), value: "view_all" }, { name: chalk_1.default.yellow("šŸ”„ Switch Mode"), value: "switch_mode" }, { name: chalk_1.default.green("šŸ“ Edit Current Config"), value: "edit" }, { name: chalk_1.default.magenta("šŸ“‚ Manage Profiles"), value: "profiles" }, { name: chalk_1.default.gray("šŸ”™ Back"), value: "back" }, ], }); switch (action) { case "view_all": await this.viewAllConfigurations(); break; case "switch_mode": await this.switchMode(); break; case "edit": await this.editCurrentConfig(); break; case "profiles": await this.manageProfiles(); break; case "back": continueMenu = false; break; } } } /** * View all configurations (Docker + Manual) */ async viewAllConfigurations() { console.clear(); console.log(chalk_1.default.cyan.bold("\nšŸ“‹ All Configurations\n")); // Get current config let currentConfig = null; try { currentConfig = await fs.readJson(this.configPath); } catch (error) { // No config } // Get all profiles const allProfiles = await this.profileManager.listProfiles(); const dockerProfiles = allProfiles.filter((p) => p.mode === config_1.SetupMode.DOCKER); const manualProfiles = allProfiles.filter((p) => p.mode === config_1.SetupMode.MANUAL); // Docker configurations console.log((0, boxen_1.default)(chalk_1.default.cyan.bold("🐳 DOCKER CONFIGURATIONS\n\n") + (dockerProfiles.length > 0 ? await this.formatProfilesList(dockerProfiles) : chalk_1.default.gray("No Docker configurations")), { padding: 1, margin: { top: 0, bottom: 1, left: 0, right: 0 }, borderStyle: "round", borderColor: "cyan", })); // Manual configurations console.log((0, boxen_1.default)(chalk_1.default.yellow.bold("āš™ļø MANUAL CONFIGURATIONS\n\n") + (manualProfiles.length > 0 ? await this.formatProfilesList(manualProfiles) : chalk_1.default.gray("No Manual configurations")), { padding: 1, margin: { top: 0, bottom: 1, left: 0, right: 0 }, borderStyle: "round", borderColor: "yellow", })); // Current active config if (currentConfig) { console.log((0, boxen_1.default)(chalk_1.default.green.bold("āœ… CURRENTLY ACTIVE\n\n") + this.formatConfigDetails(currentConfig), { padding: 1, margin: { top: 0, bottom: 1, left: 0, right: 0 }, borderStyle: "double", borderColor: "green", })); } await this.pressEnter(); } /** * Format profiles list */ async formatProfilesList(profiles) { const lines = []; for (const profile of profiles) { try { const config = await fs.readJson(profile.configPath); lines.push(chalk_1.default.white.bold(`šŸ“„ ${profile.name}`)); lines.push(chalk_1.default.dim(" Last used: ") + chalk_1.default.white(new Date(profile.lastUsedAt).toLocaleString())); lines.push(chalk_1.default.dim(" RPC: ") + chalk_1.default.white(`${config.bitcoin.host}:${config.bitcoin.port}`)); if (config.docker) { lines.push(chalk_1.default.dim(" Container: ") + chalk_1.default.cyan(config.docker.containerName)); } lines.push(""); } catch (error) { lines.push(chalk_1.default.red(`Error loading: ${profile.name}\n`)); } } return lines.join("\n"); } /** * Format config details */ formatConfigDetails(config) { const lines = []; const modeBadge = config.mode === config_1.SetupMode.DOCKER ? chalk_1.default.bgCyan.black(" 🐳 DOCKER ") : chalk_1.default.bgYellow.black(" āš™ļø MANUAL "); lines.push(chalk_1.default.white("Mode: ") + modeBadge + "\n"); lines.push(chalk_1.default.white.bold("šŸ“” Bitcoin RPC:")); lines.push(chalk_1.default.dim(" Protocol: ") + chalk_1.default.white(config.bitcoin.protocol)); lines.push(chalk_1.default.dim(" Host: ") + chalk_1.default.white(config.bitcoin.host)); lines.push(chalk_1.default.dim(" Port: ") + chalk_1.default.cyan(config.bitcoin.port.toString())); lines.push(chalk_1.default.dim(" User: ") + chalk_1.default.white(config.bitcoin.user)); lines.push(chalk_1.default.dim(" Data Dir: ") + chalk_1.default.white(config.bitcoin.dataDir)); lines.push(""); lines.push(chalk_1.default.white.bold("šŸ“ Directories:")); lines.push(chalk_1.default.dim(" App: ") + chalk_1.default.white(config.appDir)); lines.push(chalk_1.default.dim(" Wallets: ") + chalk_1.default.white(config.caravanDir)); lines.push(chalk_1.default.dim(" Keys: ") + chalk_1.default.white(config.keysDir)); if (config.mode === config_1.SetupMode.DOCKER && config.docker) { lines.push(""); lines.push(chalk_1.default.white.bold("🐳 Docker:")); lines.push(chalk_1.default.dim(" Container: ") + chalk_1.default.cyan(config.docker.containerName)); lines.push(chalk_1.default.dim(" Image: ") + chalk_1.default.white(config.docker.image)); lines.push(chalk_1.default.dim(" Network: ") + chalk_1.default.white(config.docker.network)); lines.push(chalk_1.default.dim(" RPC Port: ") + chalk_1.default.white(config.docker.ports.rpc.toString())); lines.push(chalk_1.default.dim(" P2P Port: ") + chalk_1.default.white(config.docker.ports.p2p.toString())); lines.push(chalk_1.default.dim(" Bitcoin Data: ") + chalk_1.default.white(config.docker.volumes.bitcoinData)); } return lines.join("\n"); } /** * Switch between modes */ async switchMode() { console.clear(); console.log(chalk_1.default.cyan.bold("\nšŸ”„ Switch Mode\n")); let currentConfig; try { currentConfig = await fs.readJson(this.configPath); } catch (error) { console.log(chalk_1.default.red("No current configuration found")); await this.pressEnter(); return; } const currentMode = currentConfig.mode; const targetMode = currentMode === config_1.SetupMode.DOCKER ? config_1.SetupMode.MANUAL : config_1.SetupMode.DOCKER; const currentBadge = currentMode === config_1.SetupMode.DOCKER ? chalk_1.default.bgCyan.black(" 🐳 Docker ") : chalk_1.default.bgYellow.black(" āš™ļø Manual "); const targetBadge = targetMode === config_1.SetupMode.DOCKER ? chalk_1.default.bgCyan.black(" 🐳 Docker ") : chalk_1.default.bgYellow.black(" āš™ļø Manual "); console.log(chalk_1.default.white("Current: ") + currentBadge); console.log(chalk_1.default.white("Switch to: ") + targetBadge + "\n"); // Check for existing configs in target mode const targetProfiles = await this.profileManager.getProfilesByMode(targetMode); if (targetProfiles.length > 0) { console.log(chalk_1.default.green(`Found ${targetProfiles.length} existing ${targetMode} config(s)\n`)); const choice = await (0, prompts_1.select)({ message: "Select action:", choices: [ ...targetProfiles.map((p) => ({ name: `šŸ“‚ Use: ${p.name}`, value: p.id, })), { name: chalk_1.default.cyan("āž• Create new configuration"), value: "new" }, { name: chalk_1.default.gray("šŸ”™ Cancel"), value: "cancel" }, ], }); if (choice === "cancel") return; if (choice === "new") { await this.createAndSwitchToNew(targetMode); } else { // Load selected profile const profile = await this.profileManager.getProfile(choice); if (profile) { await fs.writeJson(this.configPath, profile.config, { spaces: 2 }); await this.profileManager.setActiveProfile(profile.id); console.log(chalk_1.default.green(`\nāœ… Switched to: ${profile.name}`)); console.log(chalk_1.default.yellow("\nRestart Caravan-X for changes to take effect.")); await this.pressEnter(); } } } else { console.log(chalk_1.default.yellow(`No ${targetMode} configurations found.\n`)); const create = await (0, prompts_1.confirm)({ message: `Create a new ${targetMode} configuration?`, default: true, }); if (create) { await this.createAndSwitchToNew(targetMode); } } } /** * Create new config and switch to it */ async createAndSwitchToNew(mode) { const wizard = new setupWizard_1.SetupWizard(this.appDir); let config; if (mode === config_1.SetupMode.DOCKER) { config = await wizard.setupDockerMode(true); // Skip docker start } else { config = await wizard.setupManualMode(); } const profileName = await (0, prompts_1.input)({ message: "Name this configuration:", default: `${mode === config_1.SetupMode.DOCKER ? "Docker" : "Manual"} Config`, }); const profile = await this.profileManager.createProfile(profileName, mode, config); await fs.writeJson(this.configPath, config, { spaces: 2 }); await this.profileManager.setActiveProfile(profile.id); console.log(chalk_1.default.green(`\nāœ… Created and switched to: ${profileName}`)); console.log(chalk_1.default.yellow("\nRestart Caravan-X for changes to take effect.")); await this.pressEnter(); } /** * Edit current configuration */ async editCurrentConfig() { console.clear(); console.log(chalk_1.default.cyan.bold("\nšŸ“ Edit Current Configuration\n")); let config; try { config = await fs.readJson(this.configPath); } catch (error) { console.log(chalk_1.default.red("No configuration found")); await this.pressEnter(); return; } const choice = await (0, prompts_1.select)({ message: "What to edit?", choices: [ { name: "šŸ“” RPC Settings", value: "rpc" }, { name: "šŸ“ Directories", value: "dirs" }, ...(config.mode === config_1.SetupMode.DOCKER ? [{ name: "🐳 Docker Settings", value: "docker" }] : []), { name: chalk_1.default.gray("šŸ”™ Back"), value: "back" }, ], }); if (choice === "back") return; if (choice === "rpc") { config.bitcoin.host = await (0, prompts_1.input)({ message: "Host:", default: config.bitcoin.host, }); config.bitcoin.port = parseInt(await (0, prompts_1.input)({ message: "Port:", default: config.bitcoin.port.toString(), })); config.bitcoin.user = await (0, prompts_1.input)({ message: "User:", default: config.bitcoin.user, }); config.bitcoin.pass = await (0, prompts_1.input)({ message: "Password:", default: config.bitcoin.pass, }); } if (choice === "dirs") { config.caravanDir = await (0, prompts_1.input)({ message: "Wallets directory:", default: config.caravanDir, }); config.keysDir = await (0, prompts_1.input)({ message: "Keys directory:", default: config.keysDir, }); await fs.ensureDir(config.caravanDir); await fs.ensureDir(config.keysDir); } if (choice === "docker" && config.docker) { config.docker.containerName = await (0, prompts_1.input)({ message: "Container name:", default: config.docker.containerName, }); config.docker.ports.rpc = parseInt(await (0, prompts_1.input)({ message: "RPC port:", default: config.docker.ports.rpc.toString(), })); config.docker.ports.p2p = parseInt(await (0, prompts_1.input)({ message: "P2P port:", default: config.docker.ports.p2p.toString(), })); } await fs.writeJson(this.configPath, config, { spaces: 2 }); console.log(chalk_1.default.green("\nāœ… Configuration updated!")); if (choice === "docker") { console.log(chalk_1.default.yellow("Restart Docker containers for changes to take effect.")); } await this.pressEnter(); } /** * Manage saved profiles */ async manageProfiles() { console.clear(); console.log(chalk_1.default.cyan.bold("\nšŸ“‚ Manage Profiles\n")); const profiles = await this.profileManager.listProfiles(); if (profiles.length === 0) { console.log(chalk_1.default.gray("No saved profiles.")); await this.pressEnter(); return; } const choice = await (0, prompts_1.select)({ message: "Select profile:", choices: [ ...profiles.map((p) => ({ name: `${p.mode === config_1.SetupMode.DOCKER ? "🐳" : "āš™ļø "} ${p.name}`, value: p.id, })), { name: chalk_1.default.gray("šŸ”™ Back"), value: "back" }, ], }); if (choice === "back") return; // Manage selected profile const profile = await this.profileManager.getProfile(choice); if (!profile) return; console.log((0, boxen_1.default)(chalk_1.default.white.bold(`šŸ“„ ${profile.name}\n\n`) + this.formatConfigDetails(profile.config), { padding: 1, margin: 1, borderStyle: "round", borderColor: profile.mode === config_1.SetupMode.DOCKER ? "cyan" : "yellow", })); const action = await (0, prompts_1.select)({ message: "Action:", choices: [ { name: chalk_1.default.green("āœ… Set as Active"), value: "activate" }, { name: chalk_1.default.yellow("āœļø Rename"), value: "rename" }, { name: chalk_1.default.red("šŸ—‘ļø Delete"), value: "delete" }, { name: chalk_1.default.gray("šŸ”™ Back"), value: "back" }, ], }); if (action === "activate") { await fs.writeJson(this.configPath, profile.config, { spaces: 2 }); await this.profileManager.setActiveProfile(profile.id); console.log(chalk_1.default.green(`\nāœ… ${profile.name} is now active!`)); console.log(chalk_1.default.yellow("Restart Caravan-X for changes to take effect.")); } if (action === "rename") { const newName = await (0, prompts_1.input)({ message: "New name:", default: profile.name, }); await this.profileManager.renameProfile(profile.id, newName); console.log(chalk_1.default.green(`\nāœ… Renamed to: ${newName}`)); } if (action === "delete") { const confirmDel = await (0, prompts_1.confirm)({ message: `Delete "${profile.name}"?`, default: false, }); if (confirmDel) { await this.profileManager.deleteProfile(profile.id); console.log(chalk_1.default.green("\nāœ… Deleted")); } } await this.pressEnter(); } async pressEnter() { await (0, prompts_1.input)({ message: chalk_1.default.gray("Press Enter to continue...") }); } } exports.SettingsCommands = SettingsCommands;