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
272 lines (271 loc) • 10.1 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.initializeCaravanX = initializeCaravanX;
const inquirer_1 = __importDefault(require("inquirer"));
const chalk_1 = __importDefault(require("chalk"));
const boxen_1 = __importDefault(require("boxen"));
const ora_1 = __importDefault(require("ora"));
const config_manager_1 = require("./config-manager");
const docker_manager_1 = require("./docker-manager");
const bitcoin_rpc_1 = require("./bitcoin-rpc");
const logger_1 = require("../utils/logger");
async function initializeCaravanX() {
const configManager = new config_manager_1.ConfigManager();
// Check if config exists
const existingConfig = await configManager.loadConfig();
if (existingConfig) {
const { shouldReconfigure } = await inquirer_1.default.prompt([
{
type: "confirm",
name: "shouldReconfigure",
message: `Found existing ${chalk_1.default.cyan(existingConfig.mode)} mode configuration. Reconfigure?`,
default: false,
},
]);
if (!shouldReconfigure) {
return existingConfig;
}
}
// Mode selection
const { mode } = await inquirer_1.default.prompt([
{
type: "list",
name: "mode",
message: "Select operation mode:",
choices: [
{
name: `${chalk_1.default.cyan("🐳 Docker Mode")} - Automated regtest environment (Recommended)`,
value: "docker",
},
{
name: `${chalk_1.default.yellow("⚙️ Manual Mode")} - Use your own Bitcoin Core node`,
value: "manual",
},
],
},
]);
let config;
if (mode === "docker") {
config = await setupDockerMode();
}
else {
config = await setupManualMode();
}
await configManager.saveConfig(config);
return config;
}
async function setupDockerMode() {
console.log((0, boxen_1.default)(chalk_1.default.white.bold("Docker Mode Setup\n\n") +
chalk_1.default.gray("Caravan-X will:\n") +
chalk_1.default.white(" • Create a Bitcoin Core regtest container\n") +
chalk_1.default.white(" • Set up nginx reverse proxy\n") +
chalk_1.default.white(" • Configure RPC authentication\n") +
chalk_1.default.white(" • Generate initial blockchain\n\n") +
chalk_1.default.cyan("After setup, run your Caravan coordinator locally:\n") +
chalk_1.default.yellow(" cd caravan && npm start\n") +
chalk_1.default.cyan("Then connect to: ") +
chalk_1.default.green.bold("http://localhost:5173/"), {
padding: 1,
margin: 1,
borderStyle: "round",
borderColor: "cyan",
}));
const { proceed } = await inquirer_1.default.prompt([
{
type: "confirm",
name: "proceed",
message: "Proceed with Docker setup?",
default: true,
},
]);
if (!proceed) {
logger_1.logger.info("Docker setup cancelled");
process.exit(0);
}
const dockerManager = new docker_manager_1.DockerManager();
const spinner = (0, ora_1.default)("Setting up Docker environment...").start();
try {
// Check Docker availability
await dockerManager.checkDocker();
spinner.text = "Creating Bitcoin Core container...";
// Setup container
const containerInfo = await dockerManager.setupContainer();
spinner.text = "Configuring nginx proxy...";
// Setup nginx
await dockerManager.setupNginx();
spinner.text = "Initializing blockchain...";
// Initialize regtest chain
await dockerManager.initializeBlockchain();
spinner.succeed(chalk_1.default.green("Docker environment ready!"));
return {
mode: "docker",
docker: {
containerId: containerInfo.containerId,
rpcPort: containerInfo.rpcPort,
p2pPort: containerInfo.p2pPort,
nginxPort: 8332,
},
bitcoin: {
host: "localhost",
port: 8332,
username: containerInfo.username,
password: containerInfo.password,
network: "regtest",
},
caravan: {
coordinatorUrl: "http://localhost:5173",
},
};
}
catch (error) {
spinner.fail(chalk_1.default.red("Docker setup failed"));
throw error;
}
}
async function setupManualMode() {
const configManager = new config_manager_1.ConfigManager();
const existingBitcoinConfig = await configManager.loadBitcoinConfig();
if (existingBitcoinConfig) {
console.log((0, boxen_1.default)(chalk_1.default.white.bold("Existing Bitcoin Configuration:\n\n") +
chalk_1.default.gray("Host: ") +
chalk_1.default.white(existingBitcoinConfig.host) +
"\n" +
chalk_1.default.gray("Port: ") +
chalk_1.default.white(existingBitcoinConfig.port) +
"\n" +
chalk_1.default.gray("Network: ") +
chalk_1.default.white(existingBitcoinConfig.network) +
"\n" +
chalk_1.default.gray("Username: ") +
chalk_1.default.white(existingBitcoinConfig.username), {
padding: 1,
margin: 1,
borderStyle: "round",
borderColor: "yellow",
}));
const { useExisting } = await inquirer_1.default.prompt([
{
type: "confirm",
name: "useExisting",
message: "Use existing configuration?",
default: true,
},
]);
if (useExisting) {
// Test connection
const isConnected = await testBitcoinConnection(existingBitcoinConfig);
if (!isConnected) {
console.log((0, boxen_1.default)(chalk_1.default.red.bold("⚠️ Connection Failed\n\n") +
chalk_1.default.white("Make sure your Bitcoin Core node is running:\n") +
chalk_1.default.yellow(" bitcoind -regtest -daemon\n\n") +
chalk_1.default.gray("Then restart Caravan-X"), {
padding: 1,
margin: 1,
borderStyle: "round",
borderColor: "red",
}));
process.exit(1);
}
return {
mode: "manual",
bitcoin: existingBitcoinConfig,
caravan: {
coordinatorUrl: "http://localhost:5173",
},
};
}
}
// Get new configuration
console.log((0, boxen_1.default)(chalk_1.default.yellow.bold("⚠️ Important Notice\n\n") +
chalk_1.default.white("Manual mode requires you to:\n") +
chalk_1.default.gray(" • Run Bitcoin Core yourself\n") +
chalk_1.default.gray(" • Manage blockchain state\n") +
chalk_1.default.gray(" • Handle RPC configuration\n\n") +
chalk_1.default.cyan("Caravan-X will NOT manage your node"), {
padding: 1,
margin: 1,
borderStyle: "round",
borderColor: "yellow",
}));
const answers = await inquirer_1.default.prompt([
{
type: "input",
name: "host",
message: "Bitcoin RPC host:",
default: "localhost",
},
{
type: "number",
name: "port",
message: "Bitcoin RPC port:",
default: 18443,
},
{
type: "input",
name: "username",
message: "RPC username:",
default: "bitcoin",
},
{
type: "password",
name: "password",
message: "RPC password:",
mask: "*",
},
{
type: "list",
name: "network",
message: "Bitcoin network:",
choices: ["regtest", "testnet", "mainnet"],
default: "regtest",
},
]);
const bitcoinConfig = {
host: answers.host,
port: answers.port,
username: answers.username,
password: answers.password,
network: answers.network,
};
// Test connection
const spinner = (0, ora_1.default)("Testing connection to Bitcoin Core...").start();
const isConnected = await testBitcoinConnection(bitcoinConfig);
if (!isConnected) {
spinner.fail(chalk_1.default.red("Connection failed"));
console.log((0, boxen_1.default)(chalk_1.default.red.bold("⚠️ Cannot Connect to Bitcoin Core\n\n") +
chalk_1.default.white("Please ensure:\n") +
chalk_1.default.gray(" • Bitcoin Core is running\n") +
chalk_1.default.gray(" • RPC credentials are correct\n") +
chalk_1.default.gray(" • Network settings match\n\n") +
chalk_1.default.yellow("Start your node with:\n") +
chalk_1.default.cyan(` bitcoind -${answers.network} -daemon`), {
padding: 1,
margin: 1,
borderStyle: "round",
borderColor: "red",
}));
process.exit(1);
}
spinner.succeed(chalk_1.default.green("Connected to Bitcoin Core"));
return {
mode: "manual",
bitcoin: bitcoinConfig,
caravan: {
coordinatorUrl: "http://localhost:5173",
},
};
}
async function testBitcoinConnection(config) {
try {
const rpc = new bitcoin_rpc_1.BitcoinRPC(config);
await rpc.getBlockchainInfo();
return true;
}
catch (error) {
logger_1.logger.debug("Connection test failed:", error);
return false;
}
}