UNPKG

beeline-cli

Version:

A terminal wallet for the Hive blockchain - type, sign, rule the chain

395 lines 17.9 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; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.SimplePluginManager = void 0; exports.getPluginManager = getPluginManager; const fs = __importStar(require("fs-extra")); const path = __importStar(require("path")); const neon_js_1 = require("./neon.js"); // Simple plugin manager class SimplePluginManager { constructor() { this.plugins = new Map(); this.registeredCommands = new Map(); this.pluginDir = path.join(process.env.HOME || '', '.beeline', 'plugins'); } async initialize() { await fs.ensureDir(this.pluginDir); await this.loadAllPlugins(); } // Install a plugin from a local directory async installPlugin(sourcePath) { const theme = await (0, neon_js_1.getTheme)(); try { // Resolve source path const resolvedSource = path.resolve(sourcePath); // Check if source exists and has package.json const packagePath = path.join(resolvedSource, 'package.json'); if (!await fs.pathExists(packagePath)) { throw new Error('No package.json found in plugin directory'); } const packageJson = await fs.readJson(packagePath); const pluginName = packageJson.name; if (!pluginName) { throw new Error('Plugin package.json must have a name field'); } // Copy plugin to plugins directory const targetPath = path.join(this.pluginDir, pluginName); if (await fs.pathExists(targetPath)) { console.log(theme.chalk.warning(`${neon_js_1.neonSymbols.warning} Plugin ${pluginName} already exists, updating...`)); await fs.remove(targetPath); } await fs.copy(resolvedSource, targetPath); // Load the plugin await this.loadPlugin(targetPath); console.log(theme.chalk.success(`${neon_js_1.neonSymbols.check} Plugin installed: ${pluginName}`)); } catch (error) { console.log(theme.chalk.error(`${neon_js_1.neonSymbols.cross} Failed to install plugin: ${error instanceof Error ? error.message : 'Unknown error'}`)); throw error; } } // Load all plugins from plugin directory with better isolation async loadAllPlugins() { try { const entries = await fs.readdir(this.pluginDir); for (const entry of entries) { const pluginPath = path.join(this.pluginDir, entry); const stat = await fs.stat(pluginPath); if (stat.isDirectory()) { try { // Skip if plugin is already loaded to prevent conflicts const packagePath = path.join(pluginPath, 'package.json'); if (await fs.pathExists(packagePath)) { const packageJson = await fs.readJson(packagePath); if (this.plugins.has(packageJson.name)) { continue; // Skip already loaded plugin } } await this.loadPlugin(pluginPath); } catch (error) { const theme = await (0, neon_js_1.getTheme)(); console.log(theme.chalk.error(`${neon_js_1.neonSymbols.cross} Failed to load plugin ${entry}: ${error instanceof Error ? error.message : 'Unknown error'}`)); } } } } catch (error) { // Plugin directory doesn't exist or other error - that's ok } } // Load a single plugin with enhanced validation async loadPlugin(pluginPath) { const packagePath = path.join(pluginPath, 'package.json'); // Validate package.json exists and is readable if (!await fs.pathExists(packagePath)) { throw new Error('Plugin package.json not found'); } let packageJson; try { packageJson = await fs.readJson(packagePath); } catch (error) { throw new Error(`Invalid package.json: ${error instanceof Error ? error.message : 'Parse error'}`); } // Validate required package.json fields if (!packageJson.name || typeof packageJson.name !== 'string') { throw new Error('Plugin package.json must have a valid name field'); } if (!packageJson.version || typeof packageJson.version !== 'string') { throw new Error('Plugin package.json must have a valid version field'); } // Check if plugin is already loaded (prevent conflicts) if (this.plugins.has(packageJson.name)) { throw new Error(`Plugin ${packageJson.name} is already loaded`); } // Find and validate main file const mainFile = packageJson.main || 'index.js'; const mainPath = path.join(pluginPath, mainFile); if (!await fs.pathExists(mainPath)) { throw new Error(`Plugin main file not found: ${mainFile}`); } // Import the plugin with timeout protection let pluginModule; try { const importPromise = Promise.resolve(`${mainPath}`).then(s => __importStar(require(s))); const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error('Plugin import timeout (30s)')), 30000)); pluginModule = await Promise.race([importPromise, timeoutPromise]); } catch (error) { throw new Error(`Failed to import plugin: ${error instanceof Error ? error.message : 'Import error'}`); } const plugin = pluginModule.default || pluginModule; // Comprehensive plugin validation if (!plugin || typeof plugin !== 'object') { throw new Error('Plugin must export an object'); } if (!plugin.name || typeof plugin.name !== 'string') { throw new Error('Plugin must have a valid name property'); } if (!plugin.description || typeof plugin.description !== 'string') { throw new Error('Plugin must have a valid description property'); } if (!plugin.version || typeof plugin.version !== 'string') { throw new Error('Plugin must have a valid version property'); } if (!plugin.activate || typeof plugin.activate !== 'function') { throw new Error('Plugin must have an activate() function'); } // Validate plugin name matches package.json if (plugin.name !== packageJson.name) { throw new Error(`Plugin name "${plugin.name}" doesn't match package.json name "${packageJson.name}"`); } // Create context for plugin const context = this.createPluginContext(plugin.name); // Activate plugin with timeout and error handling try { const activatePromise = plugin.activate(context); if (activatePromise instanceof Promise) { const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error('Plugin activation timeout (15s)')), 15000)); await Promise.race([activatePromise, timeoutPromise]); } } catch (error) { throw new Error(`Plugin activation failed: ${error instanceof Error ? error.message : 'Activation error'}`); } // Store loaded plugin this.plugins.set(plugin.name, { plugin, context, path: pluginPath, packageJson }); // Only show loading message in verbose mode to reduce noise if (process.env.BEELINE_VERBOSE || process.env.DEBUG) { const theme = await (0, neon_js_1.getTheme)(); console.log(theme.chalk.success(`${neon_js_1.neonSymbols.check} Loaded plugin: ${plugin.name} v${plugin.version}`)); } } // Create context for plugin createPluginContext(pluginName) { return { addCommand: (name, description, handler) => { this.registeredCommands.set(name, { name, description, handler, pluginName }); }, addUICommand: (name, description, handler) => { this.registeredCommands.set(name, { name, description, handler: async (args, flags) => { // UI commands need special context - this will be enhanced later await handler(args, flags, null); }, pluginName, isUI: true, uiHandler: handler }); }, log: (message) => { if (process.env.BEELINE_VERBOSE || process.env.DEBUG) { console.log(`[${pluginName}] ${message}`); } }, success: (message) => { console.log(`\x1b[32m[${pluginName}] ${message}\x1b[0m`); }, error: (message) => { console.log(`\x1b[31m[${pluginName}] ${message}\x1b[0m`); }, ui: { createForm: async (options) => { // TODO: Implement form creation throw new Error('Form creation not yet implemented'); }, showDialog: async (options) => { // TODO: Implement dialog throw new Error('Dialog not yet implemented'); }, showMenu: async (options) => { // TODO: Implement menu throw new Error('Menu not yet implemented'); }, blessed: (() => { try { return require('blessed'); } catch (error) { return null; } })() }, wallet: { getCurrentAccount: async () => { try { const { KeyManager } = require('./crypto.js'); const keyManager = new KeyManager(); await keyManager.initialize(); return keyManager.getDefaultAccount() || null; } catch { return null; } }, getAccountList: async () => { try { const { KeyManager } = require('./crypto.js'); const keyManager = new KeyManager(); await keyManager.initialize(); return await keyManager.listAccounts(); } catch { return []; } }, getBalance: async (account) => { const { HiveClient } = await Promise.resolve().then(() => __importStar(require('./hive.js'))); const { KeyManager } = await Promise.resolve().then(() => __importStar(require('./crypto.js'))); const keyManager = new KeyManager(); await keyManager.initialize(); const targetAccount = account || keyManager.getDefaultAccount(); if (!targetAccount) { throw new Error('No account specified and no default account'); } const hiveClient = new HiveClient(keyManager); return hiveClient.getBalance(targetAccount); }, broadcastCustomJson: async (account, id, json, requiredAuths = [], requiredPostingAuths = []) => { const { HiveClient } = await Promise.resolve().then(() => __importStar(require('./hive.js'))); const { KeyManager } = await Promise.resolve().then(() => __importStar(require('./crypto.js'))); const keyManager = new KeyManager(); await keyManager.initialize(); const hiveClient = new HiveClient(keyManager); // First try without PIN (for unencrypted keys) try { return await hiveClient.broadcastCustomJson(account, id, json, requiredAuths, requiredPostingAuths); } catch (error) { // If it fails because PIN is required, prompt for PIN using inquirer (same as main commands) if (error.message.includes('PIN required')) { const { default: inquirer } = await Promise.resolve().then(() => __importStar(require('inquirer'))); const pinPrompt = await inquirer.prompt([{ type: 'password', name: 'pin', message: `🔐 Enter PIN to decrypt ${requiredAuths.length > 0 ? 'active' : 'posting'} key for @${account}:`, validate: (input) => input.length > 0 || 'PIN required' }]); const pin = pinPrompt.pin; // Clean up inquirer to prevent hanging if (process.stdin && process.stdin.destroy) { process.stdin.pause(); } // Retry with PIN try { return await hiveClient.broadcastCustomJson(account, id, json, requiredAuths, requiredPostingAuths, pin); } catch (pinError) { throw new Error(`Transaction failed: ${pinError.message}`); } } throw error; } } } }; } // Get list of installed plugins getPlugins() { return Array.from(this.plugins.values()); } // Get registered commands getCommands() { return this.registeredCommands; } // Execute a plugin command async executeCommand(commandName, args, flags) { const command = this.registeredCommands.get(commandName); if (!command) { throw new Error(`Command not found: ${commandName}`); } try { await command.handler(args, flags); } catch (error) { const theme = await (0, neon_js_1.getTheme)(); console.log(theme.chalk.error(`${neon_js_1.neonSymbols.cross} Plugin command failed: ${error instanceof Error ? error.message : 'Unknown error'}`)); throw error; } } // Uninstall a plugin async uninstallPlugin(pluginName) { const theme = await (0, neon_js_1.getTheme)(); const plugin = this.plugins.get(pluginName); if (!plugin) { throw new Error(`Plugin not found: ${pluginName}`); } try { // Deactivate plugin if (plugin.plugin.deactivate) { await plugin.plugin.deactivate(); } // Remove registered commands for (const [cmdName, cmd] of this.registeredCommands.entries()) { if (cmd.pluginName === pluginName) { this.registeredCommands.delete(cmdName); } } // Remove from memory this.plugins.delete(pluginName); // Remove files await fs.remove(plugin.path); console.log(theme.chalk.success(`${neon_js_1.neonSymbols.check} Plugin uninstalled: ${pluginName}`)); } catch (error) { console.log(theme.chalk.error(`${neon_js_1.neonSymbols.cross} Failed to uninstall plugin: ${error instanceof Error ? error.message : 'Unknown error'}`)); throw error; } } } exports.SimplePluginManager = SimplePluginManager; // Global instance let pluginManager = null; function getPluginManager() { if (!pluginManager) { pluginManager = new SimplePluginManager(); } return pluginManager; } //# sourceMappingURL=simple-plugins.js.map