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
286 lines (280 loc) • 12.2 kB
JavaScript
;
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.addScriptCommandsToCLI = addScriptCommandsToCLI;
const scripting_1 = require("../types/scripting");
const fs = __importStar(require("fs-extra"));
const ora_1 = __importDefault(require("ora"));
const terminal_1 = require("../utils/terminal");
/**
* Add script commands to the CLI
* @param program The Commander program to add commands to
* @param scriptEngine The script engine instance
*/
function addScriptCommandsToCLI(program, scriptEngine) {
// List script templates command
program
.command("list-templates")
.description("List available script templates")
.action(async () => {
const spinner = (0, ora_1.default)("Loading templates...").start();
try {
const templates = await scriptEngine.getScriptTemplates();
spinner.succeed("Templates loaded");
if (templates.length === 0) {
console.log((0, terminal_1.formatWarning)("No script templates found."));
return;
}
console.log((0, terminal_1.formatSuccess)(`Found ${templates.length} script templates:`));
templates.forEach((template, index) => {
console.log(`${index + 1}. ${template.name}`);
console.log(` ${template.description?.substring(0, 80) || "No description"}${template.description?.length > 80 ? "..." : ""}`);
console.log(` Type: ${template.path.endsWith(".js") ? "JavaScript" : "JSON"}`);
console.log();
});
}
catch (error) {
spinner.fail("Error loading templates");
console.error((0, terminal_1.formatError)("Error:"), error);
}
});
// Run script command
program
.command("run-script")
.description("Run a Bitcoin scenario script")
.option("-f, --file <path>", "Path to script file")
.option("-t, --template <name>", "Name of template to run")
.option("-d, --dry-run", "Run in dry-run mode (no actual execution)")
.option("-v, --verbose", "Enable verbose logging")
.option("-i, --interactive", "Run in interactive mode (confirm each step)")
.action(async (options) => {
try {
let scriptPath;
let scriptContent;
let scriptName;
if (options.file) {
// Load from file
scriptPath = options.file;
if (!fs.existsSync(scriptPath)) {
console.error((0, terminal_1.formatError)(`Script file not found: ${scriptPath}`));
return;
}
scriptName = scriptPath;
}
else if (options.template) {
// Load from template
const spinner = (0, ora_1.default)("Loading templates...").start();
const templates = await scriptEngine.getScriptTemplates();
spinner.succeed("Templates loaded");
// Find the template by name
const template = templates.find((t) => t.name.toLowerCase() === options.template.toLowerCase() ||
t.name.toLowerCase().includes(options.template.toLowerCase()));
if (!template) {
console.error((0, terminal_1.formatError)(`Template not found: ${options.template}`));
console.log((0, terminal_1.formatWarning)("Available templates:"));
templates.forEach((t) => console.log(`- ${t.name}`));
return;
}
scriptPath = template.path;
scriptName = template.name;
}
else {
console.error((0, terminal_1.formatError)("Either --file or --template option is required"));
return;
}
// Load the script
const loadingSpinner = (0, ora_1.default)(`Loading script from ${scriptPath}...`).start();
scriptContent = await scriptEngine.loadScript(scriptPath);
loadingSpinner.succeed("Script loaded");
// Validate the script
const validationSpinner = (0, ora_1.default)("Validating script...").start();
const validationResult = scriptEngine.validateScript(scriptContent);
if (!validationResult.valid) {
validationSpinner.fail("Script validation failed");
console.log((0, terminal_1.formatError)("Validation errors:"));
validationResult.errors.forEach((error) => {
console.log(` - ${error}`);
});
return;
}
validationSpinner.succeed("Script is valid");
// Generate summary
const summary = scriptEngine.generateScriptSummary(scriptContent);
console.log((0, terminal_1.boxText)(summary, {
title: `Script Summary: ${scriptName}`,
titleColor: (str) => str,
}));
// Execute the script
const executionSpinner = (0, ora_1.default)("Executing script...").start();
// Set up event handlers
scriptEngine.on("progress", (progress) => {
executionSpinner.text = `Executing step ${progress.step}/${progress.total}: ${progress.message}`;
});
scriptEngine.on("log", (message) => {
// Only show logs in verbose mode
if (options.verbose) {
executionSpinner.stop();
console.log(message);
executionSpinner.start();
}
});
try {
const result = await scriptEngine.executeScript(scriptContent, {
dryRun: options.dryRun || false,
verbose: options.verbose || false,
interactive: options.interactive || false,
});
executionSpinner.succeed("Script execution completed");
// Display results summary
console.log((0, terminal_1.boxText)(`Status: ${result.status}\n` +
`Duration: ${(result.duration / 1000).toFixed(2)} seconds\n` +
`Steps completed: ${result.steps.filter((s) => s.status === "success").length} of ${result.steps.length}`, { title: "Execution Results", titleColor: (str) => str }));
}
catch (error) {
executionSpinner.fail("Script execution failed");
console.error((0, terminal_1.formatError)(`Error: ${error.message}`));
}
}
catch (error) {
console.error((0, terminal_1.formatError)("Error running script:"), error);
}
});
// Create script command
program
.command("create-script")
.description("Create a new Bitcoin scenario script")
.option("-n, --name <name>", "Script name")
.option("-d, --description <desc>", "Script description")
.option("-t, --type <type>", "Script type (js or json)", "js")
.option("-o, --output <path>", "Output file path")
.action(async (options) => {
try {
const name = options.name || "new_script";
const description = options.description || "A Bitcoin scenario script";
const type = options.type.toLowerCase() === "json"
? scripting_1.ScriptType.JSON
: scripting_1.ScriptType.JAVASCRIPT;
// Create script content
let scriptContent;
if (type === scripting_1.ScriptType.JAVASCRIPT) {
scriptContent = `/**
* @name ${name}
* @description ${description}
* @version 1.0.0
* @author Caravan Regtest Manager
*/
// Main function to run the script
async function runScript() {
try {
console.log('Running script: ${name}');
// Create a wallet
await bitcoinService.createWallet('${name.replace(/\s+/g, "_").toLowerCase()}_wallet', {
disablePrivateKeys: false
});
// Add your script logic here
return {
success: true,
message: 'Script completed successfully'
};
} catch (error) {
console.error(\`Error in script: \${error.message}\`);
throw error;
}
}
// Run the script
runScript()
.then(result => {
console.log('Script completed successfully!');
console.log(result);
})
.catch(error => {
console.error('Script failed:', error.message);
});`;
}
else {
// JSON template
const template = {
name,
description,
version: "1.0.0",
variables: {
walletName: name.replace(/\s+/g, "_").toLowerCase() + "_wallet",
},
actions: [
{
type: "CREATE_WALLET",
description: "Create a wallet for testing",
params: {
name: "${walletName}",
options: {
disablePrivateKeys: false,
},
variableName: "wallet",
},
},
// Add more actions as needed
],
};
scriptContent = JSON.stringify(template, null, 2);
}
// Save the script
let outputPath;
if (options.output) {
outputPath = options.output;
}
else {
// Generate a default path
outputPath = await scriptEngine.saveScript(name, scriptContent, type);
}
const saveSpinner = (0, ora_1.default)(`Saving script to ${outputPath}...`).start();
if (options.output) {
// Ensure directory exists
await fs.ensureDir(require("path").dirname(outputPath));
// Write the file directly
await fs.writeFile(outputPath, scriptContent);
}
saveSpinner.succeed(`Script saved to: ${outputPath}`);
console.log((0, terminal_1.formatSuccess)("Script created successfully!"));
console.log("You can run it with:");
console.log(` caravan-regtest run-script --file "${outputPath}"`);
}
catch (error) {
console.error((0, terminal_1.formatError)("Error creating script:"), error);
}
});
}