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
478 lines (477 loc) • 22.8 kB
JavaScript
"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.VisualizationManager = void 0;
const path = __importStar(require("path"));
const fs = __importStar(require("fs-extra"));
const child_process_1 = require("child_process");
const open = __importStar(require("open"));
const blockchain_data_1 = require("./data/blockchain-data");
const http_server_1 = require("./server/http-server");
const canvas_1 = require("canvas");
const chalk_1 = __importDefault(require("chalk"));
/**
* Enhanced manager for blockchain visualization
*/
class VisualizationManager {
constructor(rpc, configManager, port = 3000) {
this.isRunning = false;
this.port = port;
this.appDir = configManager.getConfig().appDir;
this.staticDir = path.join(this.appDir, "visualization");
// Initialize services
this.blockchainData = new blockchain_data_1.BlockchainDataService(rpc);
// Initialize server
this.server = new http_server_1.VisualizationServer(this.blockchainData, this.staticDir, port);
// Ensure visualization directory exists
this.ensureVisualizationFiles();
}
/**
* Creates the Minecraft-style visualization files
*/
async createMinecraftVisualization() {
try {
// Create directories
await fs.ensureDir(path.join(this.staticDir, "js"));
await fs.ensureDir(path.join(this.staticDir, "js/world"));
await fs.ensureDir(path.join(this.staticDir, "assets/blocks"));
await fs.ensureDir(path.join(this.staticDir, "assets/characters"));
await fs.ensureDir(path.join(this.staticDir, "assets/items"));
await fs.ensureDir(path.join(this.staticDir, "assets/environment"));
await fs.ensureDir(path.join(this.staticDir, "assets/fonts"));
await fs.ensureDir(path.join(this.staticDir, "styles"));
// Copy main HTML file
const htmlContent = fs.readFileSync(path.join(__dirname, "static/minecraft/index.html"), "utf8");
await fs.writeFile(path.join(this.staticDir, "index-minecraft.html"), htmlContent);
// Copy CSS file
const cssContent = fs.readFileSync(path.join(__dirname, "static/minecraft/styles/minecraft-style.css"), "utf8");
await fs.writeFile(path.join(this.staticDir, "styles/minecraft-style.css"), cssContent);
const cssContent2 = fs.readFileSync(path.join(__dirname, "static/minecraft/styles/blockchain.css"), "utf8");
await fs.writeFile(path.join(this.staticDir, "styles/blockchain.css"), cssContent2);
// Copy JavaScript files
const voxelEngineContent = fs.readFileSync(path.join(__dirname, "static/minecraft/js/voxel-engine.js"), "utf8");
await fs.writeFile(path.join(this.staticDir, "js/voxel-engine.js"), voxelEngineContent);
const characterSystemContent = fs.readFileSync(path.join(__dirname, "static/minecraft/js/character-system.js"), "utf8");
await fs.writeFile(path.join(this.staticDir, "js/character-system.js"), characterSystemContent);
const blockchainIntegrationContent = fs.readFileSync(path.join(__dirname, "static/minecraft/js/blockchain-game-integration.js"), "utf8");
await fs.writeFile(path.join(this.staticDir, "js/blockchain-game-integration.js"), blockchainIntegrationContent);
const BlockchainWorldContent = fs.readFileSync(path.join(__dirname, "static/minecraft/js/world/BlockchainWorld.js"), "utf8");
await fs.writeFile(path.join(this.staticDir, "js/world/BlockchainWorld.js"), BlockchainWorldContent);
// Create assets
await this.createMinecraftAssets();
console.log(chalk_1.default.green("Minecraft-style visualization created successfully"));
}
catch (error) {
console.error("Error creating Minecraft visualization:", error);
}
}
/**
* Creates placeholder assets for the Minecraft visualization
*/
async createMinecraftAssets() {
// Helper function to create a simple colored rectangle as a placeholder
const createPlaceholderImage = async (filename, color, width, height) => {
try {
const canvas = new canvas_1.Canvas(width, height);
const ctx = canvas.getContext("2d");
// Fill background
ctx.fillStyle = color;
ctx.fillRect(0, 0, width, height);
// Add some pixel-art detail
ctx.fillStyle = "rgba(255,255,255,0.3)";
for (let x = 0; x < width; x += 4) {
for (let y = 0; y < height; y += 4) {
if (Math.random() > 0.8) {
ctx.fillRect(x, y, 2, 2);
}
}
}
const buffer = canvas.toBuffer("image/png");
await fs.writeFile(path.join(this.staticDir, filename), buffer);
}
catch (error) {
console.error(`Error creating placeholder image ${filename}:`, error);
// Fallback for environments without Canvas support
const buffer = Buffer.from([
0x89,
0x50,
0x4e,
0x47,
0x0d,
0x0a,
0x1a,
0x0a, // PNG signature
0x00,
0x00,
0x00,
0x0d, // IHDR chunk length
0x49,
0x48,
0x44,
0x52, // "IHDR"
0x00,
0x00,
0x00,
0x10, // width: 16
0x00,
0x00,
0x00,
0x10, // height: 16
0x08,
0x02,
0x00,
0x00,
0x00, // bit depth, color type, compression, filter, interlace
0x90,
0x1b,
0xd9,
0x28, // CRC
0x00,
0x00,
0x00,
0x01, // IDAT chunk length
0x49,
0x44,
0x41,
0x54, // "IDAT"
0x08, // 8 bit data
0xd7,
0x64,
0xc3,
0xd7, // CRC
0x00,
0x00,
0x00,
0x00, // IEND chunk length
0x49,
0x45,
0x4e,
0x44, // "IEND"
0xae,
0x42,
0x60,
0x82, // CRC
]);
await fs.writeFile(path.join(this.staticDir, filename), buffer);
}
};
// Create block textures
await createPlaceholderImage("assets/blocks/grass.png", "#55AA55", 16, 16);
await createPlaceholderImage("assets/blocks/stone.png", "#888888", 16, 16);
await createPlaceholderImage("assets/blocks/gold.png", "#FFCC00", 16, 16);
await createPlaceholderImage("assets/blocks/building.png", "#AA8866", 16, 16);
// Create character textures
await createPlaceholderImage("assets/characters/miner.png", "#FFAA00", 16, 32);
await createPlaceholderImage("assets/characters/transaction.png", "#00AAFF", 16, 32);
await createPlaceholderImage("assets/characters/trader.png", "#AA00FF", 16, 32);
await createPlaceholderImage("assets/characters/validator.png", "#00FFAA", 16, 32);
// Create item textures
await createPlaceholderImage("assets/items/pickaxe.png", "#8B4513", 16, 16);
await createPlaceholderImage("assets/items/shovel.png", "#A9A9A9", 16, 16);
await createPlaceholderImage("assets/items/axe.png", "#CD853F", 16, 16);
await createPlaceholderImage("assets/items/sword.png", "#C0C0C0", 16, 16);
await createPlaceholderImage("assets/items/compass.png", "#FF4500", 16, 16);
await createPlaceholderImage("assets/items/coin.png", "#FFD700", 16, 16);
// Create environment textures
await createPlaceholderImage("assets/environment/sky.png", "#87CEEB", 64, 64);
await createPlaceholderImage("assets/environment/ground.png", "#556B2F", 16, 16);
await createPlaceholderImage("assets/environment/cloud.png", "#FFFFFF", 32, 16);
// Create a placeholder font file
const emptyFont = Buffer.from([0]);
await fs.writeFile(path.join(this.staticDir, "assets/fonts/minecraft.woff2"), emptyFont);
}
/**
* Update the existing ensureVisualizationFiles method to include Minecraft visualization
*/
async ensureVisualizationFiles() {
try {
// Check if static directory exists
if (!(await fs.pathExists(this.staticDir))) {
await fs.ensureDir(this.staticDir);
// Copy standard visualization files
await this.createAdvancedVisualizationFiles();
// Create pixel art visualization files
await this.createPixelArtVisualization();
// Create Minecraft-style visualization files
await this.createMinecraftVisualization();
}
else {
// Check if we need to update existing files to the new version
const indexHtmlPath = path.join(this.staticDir, "index.html");
if (await fs.pathExists(indexHtmlPath)) {
const currentContent = await fs.readFile(indexHtmlPath, "utf8");
// Check if this is the old basic visualization
if (!currentContent.includes("socket.io") &&
!currentContent.includes("chart.js")) {
console.log(chalk_1.default.yellow("Updating visualization files to enhanced version..."));
await this.createAdvancedVisualizationFiles();
await this.createPixelArtVisualization();
await this.createMinecraftVisualization();
}
}
// Check if Minecraft visualization exists
const minecraftHtmlPath = path.join(this.staticDir, "index-minecraft.html");
if (!(await fs.pathExists(minecraftHtmlPath))) {
console.log(chalk_1.default.yellow("Adding Minecraft-style visualization..."));
await this.createMinecraftVisualization();
}
}
}
catch (error) {
console.error("Error ensuring visualization files:", error);
}
}
/**
* Creates the pixel art visualization files
*/
async createPixelArtVisualization() {
try {
// --- 1) Prepare folders ---
const assetsPixelDir = path.join(this.staticDir, "assets_pixel");
const sourceDir = path.join(__dirname, "static", "pixel");
const sourceAssetsDir = path.join(sourceDir, "assets_pixel");
await fs.ensureDir(path.join(assetsPixelDir, "characters_pixel"));
await fs.ensureDir(path.join(assetsPixelDir, "icons_pixel"));
await fs.ensureDir(path.join(assetsPixelDir, "sounds_pixel"));
await fs.ensureDir(path.join(this.staticDir, "js"));
// Copy pixel-art assets and HTML
await this.copyPixelArtAssets(sourceAssetsDir, assetsPixelDir);
await fs.copy(path.join(sourceDir, "index.html"), path.join(this.staticDir, "index-pixel.html"));
// --- 2) Create necessary type definitions ---
const typesDir = path.join(this.staticDir, "js", "types");
await fs.ensureDir(typesDir);
// Create simplified type definitions file
const typeDefContent = `
// Basic type definitions for browser globals
declare var PIXI: any;
declare var gsap: any;
declare var io: any;
declare var Howl: any;
`;
await fs.writeFile(path.join(typesDir, "globals.d.ts"), typeDefContent);
// --- 3) Copy TS source into static/js ---
const tsSourcePath = path.join(sourceDir, "pixel-city.ts");
const tsDestPath = path.join(this.staticDir, "js", "pixel-city.ts");
await fs.copy(tsSourcePath, tsDestPath);
// --- 4) Compile with simpler settings ---
console.log(chalk_1.default.blue("Compiling pixel-city.ts → pixel-city.js..."));
return new Promise((resolve) => {
const outDir = path.join(this.staticDir, "js");
// Use simpler compilation approach for browser script
(0, child_process_1.exec)(`tsc "${tsDestPath}" --outDir "${outDir}" --target ES2020 --lib DOM,ES2020 --module None --noImplicitAny false --skipLibCheck true --strictPropertyInitialization false`, { cwd: process.cwd() }, (err, stdout, stderr) => {
if (err) {
console.error(chalk_1.default.red("TypeScript error:"), stderr || err.message);
return;
}
console.log(chalk_1.default.green("pixel-city.ts compiled → js/pixel-city.js"));
resolve();
});
});
}
catch (err) {
console.error("Error in createPixelArtVisualization:", err);
}
}
/**
* Copies existing pixel art assets to the new directory structure
* @param sourceDir The source directory containing existing assets
* @param assetsPixelDir The target directory for pixel assets
*/
async copyPixelArtAssets(sourceDir, assetsPixelDir) {
try {
console.log(chalk_1.default.blue("Copying existing pixel assets to new structure..."));
// Define the mapping of source to target directories
const directoryMappings = [
// Characters
{
source: path.join(sourceDir, "characters_pixel"),
target: path.join(assetsPixelDir, "characters_pixel"),
},
// Icons
{
source: path.join(sourceDir, "icons_pixel"),
target: path.join(assetsPixelDir, "icons_pixel"),
},
// Sounds
{
source: path.join(sourceDir, "sounds_pixel"),
target: path.join(assetsPixelDir, "sounds_pixel"),
},
];
// Track total files copied for reporting
let totalFilesCopied = 0;
// Copy files for each directory mapping
for (const mapping of directoryMappings) {
try {
// Check if source directory exists
const exists = await fs.pathExists(mapping.source);
if (!exists) {
console.log(chalk_1.default.yellow(`Source directory not found: ${mapping.source}`));
continue;
}
// Get list of files in the source directory
const files = await fs.readdir(mapping.source);
// Copy each file to the target directory
for (const file of files) {
const sourcePath = path.join(mapping.source, file);
const targetPath = path.join(mapping.target, file);
// Check if source is a file (not a directory)
const stats = await fs.stat(sourcePath);
if (stats.isFile()) {
await fs.copy(sourcePath, targetPath, { overwrite: true });
totalFilesCopied++;
console.log(chalk_1.default.green(`Copied: ${file} to ${mapping.target}`));
}
else if (stats.isDirectory()) {
// Handle subdirectories if needed
await fs.copy(sourcePath, path.join(mapping.target, file), {
overwrite: true,
});
console.log(chalk_1.default.green(`Copied directory: ${file} to ${mapping.target}`));
// Count files in subdirectory
const subFiles = await fs.readdir(sourcePath);
totalFilesCopied += subFiles.length;
}
}
}
catch (error) {
console.error(chalk_1.default.red(`Error copying files from ${mapping.source}: ${error}`));
}
}
console.log(chalk_1.default.green(`Successfully copied ${totalFilesCopied} pixel art assets.`));
}
catch (error) {
console.error(chalk_1.default.red("Error copying pixel art assets:", error));
throw error; // Re-throw to allow calling function to handle the error
}
}
/**
* Creates placeholder assets for the pixel art visualization
*/
async createPixelArtAssets() {
try {
// Create empty placeholder files for assets
// In a real implementation, you would create actual pixel art assets
// Create empty sound files
const emptyBuffer = Buffer.from([]);
await fs.writeFile(path.join(this.staticDir, "sounds/block_mined.mp3"), emptyBuffer);
await fs.writeFile(path.join(this.staticDir, "sounds/transaction.mp3"), emptyBuffer);
await fs.writeFile(path.join(this.staticDir, "sounds/click.mp3"), emptyBuffer);
await fs.writeFile(path.join(this.staticDir, "sounds/blockchain_city_theme.mp3"), emptyBuffer);
console.log(chalk_1.default.green("Pixel art assets created successfully."));
}
catch (error) {
console.error("Error creating pixel art assets:", error);
}
}
/**
* Create advanced visualization files
*/
async createAdvancedVisualizationFiles() {
try {
// Create directories
await fs.ensureDir(path.join(this.staticDir, "css"));
await fs.ensureDir(path.join(this.staticDir, "js"));
await fs.ensureDir(path.join(this.staticDir, "assets"));
// Define source directory for standard visualization
const sourceDir = path.join(__dirname, "static", "standard");
// Copy HTML file
await fs.copy(path.join(sourceDir, "index.html"), path.join(this.staticDir, "index.html"));
// Copy JavaScript file
await fs.copy(path.join(sourceDir, "js", "main.js"), path.join(this.staticDir, "js", "main.js"));
// Copy CSS file
await fs.copy(path.join(sourceDir, "css", "styles.css"), path.join(this.staticDir, "css", "styles.css"));
console.log(chalk_1.default.green("Advanced visualization files created successfully."));
}
catch (error) {
console.error("Error creating advanced visualization files:", error);
}
}
/**
* Start visualization
*/
async start(openBrowser = true) {
if (this.isRunning) {
console.log(chalk_1.default.yellow("Visualization server is already running."));
return;
}
try {
await this.server.start();
this.isRunning = true;
console.log(chalk_1.default.green(`Blockchain visualization server running at http://localhost:${this.port}/`));
if (openBrowser) {
console.log(chalk_1.default.cyan("Opening visualization in browser..."));
//@ts-expect-error
await open(`http://localhost:${this.port}/`);
}
}
catch (error) {
console.error(chalk_1.default.red("Error starting visualization server:"), error);
}
}
/**
* Stop visualization
*/
async stop() {
if (!this.isRunning) {
console.log(chalk_1.default.yellow("Visualization server is not running."));
return;
}
try {
await this.server.stop();
this.isRunning = false;
console.log(chalk_1.default.green("Visualization server stopped."));
}
catch (error) {
console.error(chalk_1.default.red("Error stopping visualization server:"), error);
}
}
/**
* Check if the visualization server is running
*/
isServerRunning() {
return this.isRunning;
}
/**
* Get server URL
*/
getServerUrl() {
return `http://localhost:${this.port}/`;
}
}
exports.VisualizationManager = VisualizationManager;