UNPKG

@ghostspeak/cli

Version:

Command-line interface for GhostSpeak AI Agent Commerce Protocol - Production Ready Beta

1,525 lines (1,523 loc) 566 kB
#!/usr/bin/env node import { createRequire } from 'module'; import { config } from 'dotenv'; import path, { join, dirname, resolve } from 'path'; import { existsSync, readFileSync, writeFileSync, chmodSync, mkdirSync, unlinkSync, renameSync, promises } from 'fs'; import { URL as URL$1, fileURLToPath } from 'url'; import { createSolanaRpc, createKeyPairSignerFromBytes, createSolanaRpcSubscriptions, address as address$1, generateKeyPairSigner, getProgramDerivedAddress, getBytesEncoder, getAddressEncoder, getUtf8Encoder } from '@solana/kit'; import os, { homedir } from 'os'; import * as bip39 from 'bip39'; import { derivePath } from 'ed25519-hd-key'; import nacl from 'tweetnacl'; import { EventEmitter } from 'events'; import { Subject, filter, BehaviorSubject, merge, map } from 'rxjs'; import { EscrowModule, AuctionType, GhostSpeakClient, CredentialModule, GHOSTSPEAK_PROGRAM_ID, AgentModule, MarketplaceModule, ChannelModule, GovernanceModule } from '@ghostspeak/sdk'; import { intro, spinner, text, isCancel, cancel, select, note, confirm, outro, log, multiselect } from '@clack/prompts'; import chalk32 from 'chalk'; import { address } from '@solana/addresses'; import { Command, program } from 'commander'; import figlet from 'figlet'; import { randomUUID, scrypt, randomBytes, createCipheriv, createDecipheriv } from 'crypto'; import fs2, { chmod, access, mkdir, writeFile, readFile } from 'fs/promises'; import 'node-fetch'; import Table from 'cli-table3'; import pc from 'picocolors'; import { exec } from 'child_process'; import { promisify } from 'util'; import { Table as Table$1 } from 'console-table-printer'; import boxen from 'boxen'; import pino from 'pino'; createRequire(import.meta.url); var __defProp = Object.defineProperty; var __getOwnPropNames = Object.getOwnPropertyNames; var __esm = (fn, res) => function __init() { return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res; }; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; function loadEnvFiles() { const envLocations = [ // 1. Current working directory resolve(process.cwd(), ".env"), // 2. Project root (two levels up from CLI package) resolve(process.cwd(), "../../.env"), // 3. CLI package directory (same as this file) (() => { try { const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); return resolve(__dirname, "../../.env"); } catch (error) { return ""; } })(), // 4. Parent of CLI package directory (() => { try { const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); return resolve(__dirname, "../../../.env"); } catch (error) { return ""; } })() ].filter(Boolean); for (const envPath of envLocations) { if (existsSync(envPath)) { config({ path: envPath }); break; } } } function getCurrentNetwork() { const network = process.env.GHOSTSPEAK_NETWORK ?? "devnet"; if (!["mainnet-beta", "devnet", "testnet", "localnet"].includes(network)) { throw new Error(`Invalid network: ${network}`); } return network; } function getProgramId() { const network = getCurrentNetwork(); let programIdStr; const DEFAULT_DEVNET_PROGRAM_ID = "GHSTSPKUhJAMv3j79AFyPoYjyPHaSDe65bE8F6yEhV8s"; switch (network) { case "mainnet-beta": programIdStr = process.env.GHOSTSPEAK_PROGRAM_ID_MAINNET; break; case "devnet": programIdStr = process.env.GHOSTSPEAK_PROGRAM_ID_DEVNET ?? DEFAULT_DEVNET_PROGRAM_ID; break; case "testnet": programIdStr = process.env.GHOSTSPEAK_PROGRAM_ID_TESTNET; break; case "localnet": programIdStr = process.env.GHOSTSPEAK_PROGRAM_ID_LOCALNET; break; } if (!programIdStr) { return null; } try { return address$1(programIdStr); } catch (error) { console.warn(`Invalid program ID for ${network}: ${programIdStr}`); return null; } } function getUsdcMint() { const network = getCurrentNetwork(); let mintStr; switch (network) { case "mainnet-beta": mintStr = process.env.GHOSTSPEAK_USDC_MINT_MAINNET ?? "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"; break; case "devnet": mintStr = process.env.GHOSTSPEAK_USDC_MINT_DEVNET ?? "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU"; break; case "testnet": case "localnet": mintStr = process.env.GHOSTSPEAK_USDC_MINT_TESTNET ?? "11111111111111111111111111111111"; break; } return address$1(mintStr); } function loadEnvironmentConfig() { const network = getCurrentNetwork(); return { network, rpcUrl: process.env.GHOSTSPEAK_RPC_URL ?? getDefaultRpcUrl(network), programId: getProgramId(), walletPath: process.env.GHOSTSPEAK_WALLET_PATH ?? "~/.config/solana/ghostspeak.json", usdcMint: getUsdcMint(), debug: process.env.GHOSTSPEAK_DEBUG === "true", logLevel: process.env.GHOSTSPEAK_LOG_LEVEL ?? "info", encryptionSalt: process.env.GHOSTSPEAK_ENCRYPTION_SALT, keyDerivationIterations: parseInt(process.env.GHOSTSPEAK_KEY_DERIVATION_ITERATIONS ?? "100000", 10) }; } function getDefaultRpcUrl(network) { switch (network) { case "mainnet-beta": return "https://api.mainnet-beta.solana.com"; case "devnet": return "https://api.devnet.solana.com"; case "testnet": return "https://api.testnet.solana.com"; case "localnet": return "http://localhost:8899"; default: return "https://api.devnet.solana.com"; } } function getEnvConfig() { if (!_envConfig) { _envConfig = loadEnvironmentConfig(); } return _envConfig; } var _envConfig, envConfig; var init_env_config = __esm({ "src/utils/env-config.ts"() { loadEnvFiles(); _envConfig = null; envConfig = new Proxy({}, { get(_target, prop) { return getEnvConfig()[prop]; } }); } }); // src/utils/config.ts var config_exports = {}; __export(config_exports, { ensureConfigDir: () => ensureConfigDir, getConfigPath: () => getConfigPath, loadConfig: () => loadConfig, resetConfig: () => resetConfig, saveConfig: () => saveConfig }); function ensureConfigDir() { if (!existsSync(CONFIG_DIR)) { mkdirSync(CONFIG_DIR, { recursive: true, mode: 448 }); } } function loadConfig() { ensureConfigDir(); if (!existsSync(CONFIG_FILE)) { return DEFAULT_CONFIG; } try { const configData = readFileSync(CONFIG_FILE, "utf-8"); const config2 = JSON.parse(configData); return { ...DEFAULT_CONFIG, ...config2 }; } catch (error) { console.error("Error loading config:", error); return DEFAULT_CONFIG; } } function saveConfig(config2) { ensureConfigDir(); const currentConfig = loadConfig(); const newConfig = { ...currentConfig, ...config2 }; writeFileSync(CONFIG_FILE, JSON.stringify(newConfig, null, 2)); chmodSync(CONFIG_FILE, 384); } function resetConfig() { ensureConfigDir(); if (existsSync(CONFIG_FILE)) { writeFileSync(CONFIG_FILE, JSON.stringify(DEFAULT_CONFIG, null, 2)); chmodSync(CONFIG_FILE, 384); } } function getConfigPath() { return CONFIG_FILE; } var CONFIG_DIR, CONFIG_FILE, DEFAULT_CONFIG; var init_config = __esm({ "src/utils/config.ts"() { init_env_config(); CONFIG_DIR = join(homedir(), ".ghostspeak"); CONFIG_FILE = join(CONFIG_DIR, "config.json"); DEFAULT_CONFIG = { network: envConfig.network, walletPath: envConfig.walletPath.startsWith("~") ? join(homedir(), envConfig.walletPath.slice(2)) : envConfig.walletPath, programId: envConfig.programId.toString(), rpcUrl: envConfig.rpcUrl }; } }); var WalletService; var init_wallet_service = __esm({ "src/services/wallet-service.ts"() { WalletService = class { walletsDir; registryPath; constructor() { const ghostspeakDir = join(homedir(), ".ghostspeak"); this.walletsDir = join(ghostspeakDir, "wallets"); this.registryPath = join(this.walletsDir, "registry.json"); if (!existsSync(ghostspeakDir)) { mkdirSync(ghostspeakDir, { recursive: true }); } if (!existsSync(this.walletsDir)) { mkdirSync(this.walletsDir, { recursive: true }); } } /** * Get or create the wallets registry */ getRegistry() { if (existsSync(this.registryPath)) { try { return JSON.parse(readFileSync(this.registryPath, "utf-8")); } catch (error) { } } return { activeWallet: null, wallets: {} }; } /** * Save the registry */ saveRegistry(registry) { writeFileSync(this.registryPath, JSON.stringify(registry, null, 2)); } /** * Generate a new mnemonic seed phrase */ generateMnemonic() { return bip39.generateMnemonic(256); } /** * Create keypair from mnemonic with proper BIP44 derivation * Returns both the signer and the raw 64-byte secret key */ async keypairFromMnemonic(mnemonic, index = 0) { if (!bip39.validateMnemonic(mnemonic)) { throw new Error("Invalid mnemonic phrase"); } try { const seed = await bip39.mnemonicToSeed(mnemonic); const derivationPath = `m/44'/501'/${index}'/0'`; const { key } = derivePath(derivationPath, seed.toString("hex")); const keyPair = nacl.sign.keyPair.fromSeed(new Uint8Array(key)); const secretKey = keyPair.secretKey; const signer = await createKeyPairSignerFromBytes(secretKey); return { signer, secretKey }; } catch (error) { throw new Error(`Failed to derive keypair from mnemonic: ${error instanceof Error ? error.message : "Unknown error"}`); } } /** * Create a new wallet */ async createWallet(name, network = "devnet", mnemonic) { const registry = this.getRegistry(); if (name in registry.wallets) { throw new Error(`Wallet with name "${name}" already exists`); } const seedPhrase = mnemonic ?? this.generateMnemonic(); const { signer, secretKey } = await this.keypairFromMnemonic(seedPhrase); const walletData = { metadata: { name, address: signer.address.toString(), network, createdAt: Date.now(), lastUsed: Date.now(), isActive: Object.keys(registry.wallets).length === 0 // First wallet is active by default }, keypair: Array.from(secretKey) }; const walletPath = join(this.walletsDir, `${name}.json`); writeFileSync(walletPath, JSON.stringify(walletData, null, 2)); registry.wallets[name] = walletData.metadata; if (walletData.metadata.isActive) { registry.activeWallet = name; } this.saveRegistry(registry); return { wallet: walletData, mnemonic: seedPhrase }; } /** * Import wallet from private key or mnemonic */ async importWallet(name, secretKeyOrMnemonic, network = "devnet") { const registry = this.getRegistry(); if (name in registry.wallets) { throw new Error(`Wallet with name "${name}" already exists`); } let signer; let privateKeyBytes; if (typeof secretKeyOrMnemonic === "string") { if (bip39.validateMnemonic(secretKeyOrMnemonic)) { const result = await this.keypairFromMnemonic(secretKeyOrMnemonic); signer = result.signer; privateKeyBytes = result.secretKey; } else { try { const bytes = JSON.parse(secretKeyOrMnemonic); privateKeyBytes = new Uint8Array(bytes); if (privateKeyBytes.length === 32) { const keyPair = nacl.sign.keyPair.fromSeed(privateKeyBytes); privateKeyBytes = keyPair.secretKey; } signer = await createKeyPairSignerFromBytes(privateKeyBytes); } catch (error) { throw new Error("Invalid private key or mnemonic format"); } } } else { privateKeyBytes = secretKeyOrMnemonic; if (privateKeyBytes.length === 32) { const keyPair = nacl.sign.keyPair.fromSeed(privateKeyBytes); privateKeyBytes = keyPair.secretKey; } signer = await createKeyPairSignerFromBytes(privateKeyBytes); } const walletData = { metadata: { name, address: signer.address.toString(), network, createdAt: Date.now(), lastUsed: Date.now(), isActive: Object.keys(registry.wallets).length === 0 }, keypair: Array.from(privateKeyBytes) }; const walletPath = join(this.walletsDir, `${name}.json`); writeFileSync(walletPath, JSON.stringify(walletData, null, 2)); registry.wallets[name] = walletData.metadata; if (walletData.metadata.isActive) { registry.activeWallet = name; } this.saveRegistry(registry); return walletData; } /** * List all wallets */ listWallets() { const registry = this.getRegistry(); return Object.values(registry.wallets).sort((a, b) => b.lastUsed - a.lastUsed); } /** * Get active wallet */ getActiveWallet() { const registry = this.getRegistry(); if (!registry.activeWallet) { return null; } return this.getWallet(registry.activeWallet); } /** * Get wallet by name */ getWallet(name) { const registry = this.getRegistry(); if (!(name in registry.wallets)) { return null; } const walletPath = join(this.walletsDir, `${name}.json`); if (!existsSync(walletPath)) { return null; } try { const walletData = JSON.parse(readFileSync(walletPath, "utf-8")); walletData.metadata.lastUsed = Date.now(); registry.wallets[name].lastUsed = Date.now(); this.saveRegistry(registry); return walletData; } catch (error) { return null; } } /** * Set active wallet */ setActiveWallet(name) { const registry = this.getRegistry(); if (!(name in registry.wallets)) { throw new Error(`Wallet "${name}" not found`); } Object.keys(registry.wallets).forEach((walletName) => { registry.wallets[walletName].isActive = walletName === name; }); registry.activeWallet = name; this.saveRegistry(registry); } /** * Rename wallet */ renameWallet(oldName, newName) { const registry = this.getRegistry(); if (!(oldName in registry.wallets)) { throw new Error(`Wallet "${oldName}" not found`); } if (newName in registry.wallets) { throw new Error(`Wallet "${newName}" already exists`); } const oldPath = join(this.walletsDir, `${oldName}.json`); const newPath = join(this.walletsDir, `${newName}.json`); if (existsSync(oldPath)) { const walletData = JSON.parse(readFileSync(oldPath, "utf-8")); walletData.metadata.name = newName; writeFileSync(newPath, JSON.stringify(walletData, null, 2)); unlinkSync(oldPath); } const metadata = registry.wallets[oldName]; metadata.name = newName; registry.wallets[newName] = metadata; delete registry.wallets[oldName]; if (registry.activeWallet === oldName) { registry.activeWallet = newName; } this.saveRegistry(registry); } /** * Delete wallet */ deleteWallet(name) { const registry = this.getRegistry(); if (!(name in registry.wallets)) { throw new Error(`Wallet "${name}" not found`); } if (registry.activeWallet === name && Object.keys(registry.wallets).length > 1) { throw new Error("Cannot delete active wallet. Switch to another wallet first."); } const walletPath = join(this.walletsDir, `${name}.json`); if (existsSync(walletPath)) { unlinkSync(walletPath); } delete registry.wallets[name]; if (registry.activeWallet === name) { const remainingWallets = Object.keys(registry.wallets); registry.activeWallet = remainingWallets.length > 0 ? remainingWallets[0] : null; if (registry.activeWallet) { registry.wallets[registry.activeWallet].isActive = true; } } this.saveRegistry(registry); } /** * Get wallet balance */ async getBalance(walletAddress, network) { try { const rpcUrl = network === "devnet" ? "https://api.devnet.solana.com" : network === "testnet" ? "https://api.testnet.solana.com" : "https://api.mainnet-beta.solana.com"; const rpc = createSolanaRpc(rpcUrl); const { value: balance } = await rpc.getBalance(address$1(walletAddress)).send(); return Number(balance) / 1e9; } catch (error) { return 0; } } /** * Get signer for a wallet */ async getSigner(name) { const wallet = this.getWallet(name); if (!wallet) { return null; } return createKeyPairSignerFromBytes(new Uint8Array(wallet.keypair)); } /** * Get active signer */ async getActiveSigner() { const wallet = this.getActiveWallet(); if (!wallet) { return null; } return createKeyPairSignerFromBytes(new Uint8Array(wallet.keypair)); } /** * Interface-compatible method: Create wallet with return type for IWalletService */ async createWalletInterface(name, network) { const mnemonic = bip39.generateMnemonic(); const { wallet: walletData } = await this.createWallet(name, network, mnemonic); const walletInfo = { address: address$1(walletData.metadata.address), name: walletData.metadata.name, network: walletData.metadata.network, metadata: { createdAt: walletData.metadata.createdAt, lastUsed: walletData.metadata.lastUsed, isActive: walletData.metadata.isActive } }; return { wallet: walletInfo, mnemonic }; } /** * Interface-compatible method: Import wallet from mnemonic */ async importWalletInterface(name, mnemonic, network) { const walletData = await this.importWallet(name, mnemonic, network); return { address: address$1(walletData.metadata.address), name: walletData.metadata.name, network: walletData.metadata.network, metadata: { createdAt: walletData.metadata.createdAt, lastUsed: walletData.metadata.lastUsed, isActive: walletData.metadata.isActive } }; } /** * Interface-compatible method: List wallets as WalletInfo[] */ async listWalletsInterface() { const wallets = this.listWallets(); return wallets.map((wallet) => ({ address: address$1(wallet.address), name: wallet.name, network: wallet.network, metadata: { createdAt: wallet.createdAt, lastUsed: wallet.lastUsed, isActive: wallet.isActive } })); } /** * Interface-compatible method: Get active wallet as WalletInfo */ getActiveWalletInterface() { const wallet = this.getActiveWallet(); if (!wallet) return null; return { address: address$1(wallet.metadata.address), name: wallet.metadata.name, network: wallet.metadata.network, metadata: { createdAt: wallet.metadata.createdAt, lastUsed: wallet.metadata.lastUsed, isActive: wallet.metadata.isActive } }; } /** * Interface-compatible method: Set active wallet */ async setActiveWalletInterface(name) { this.setActiveWallet(name); } /** * Interface-compatible method: Get balance for address */ async getBalanceInterface(walletAddress) { const balance = await this.getBalance(walletAddress.toString(), "devnet"); return BigInt(Math.floor(balance * 1e9)); } /** * Interface-compatible method: Sign transaction */ async signTransaction(signer, transaction) { try { const { signTransaction: signTransactionKit } = await import('@solana/kit'); const tx = transaction; const signedTransaction = await signTransactionKit([signer], tx); const signatures = Object.values(signedTransaction.signatures); if (signatures.length > 0) { const signature = signatures[0]; if (!signature) { throw new Error("Signature is null"); } console.log(`\u2705 Transaction signed by ${signer.address.toString()}`); return signature.toString(); } else { throw new Error("No signature found in signed transaction"); } } catch (error) { console.error("Failed to sign transaction:", error); throw new Error(`Transaction signing failed: ${error instanceof Error ? error.message : "Unknown error"}`); } } /** * Migrate old wallet.json to new system */ async migrateOldWallet() { const oldWalletPath = join(homedir(), ".ghostspeak", "wallet.json"); if (!existsSync(oldWalletPath)) { return; } try { const oldWalletData = JSON.parse(readFileSync(oldWalletPath, "utf-8")); await this.importWallet("main", new Uint8Array(oldWalletData), "devnet"); renameSync(oldWalletPath, oldWalletPath + ".backup"); } catch (error) { console.warn("Failed to migrate old wallet:", error); } } }; } }); var EventBus, EventCorrelator, StreamManager, CommandResultStream, BlockchainEventListener, CLIStateManager; var init_event_system = __esm({ "src/core/event-system.ts"() { EventBus = class _EventBus extends EventEmitter { static instance = null; eventSubjects = /* @__PURE__ */ new Map(); globalSubject = new Subject(); eventHistory = []; maxHistorySize = 1e3; correlationIdCounter = 0; /** * Get singleton instance */ static getInstance() { _EventBus.instance ??= new _EventBus(); return _EventBus.instance; } /** * Emit an event */ emit(type, data, options) { const event = { type, data, timestamp: /* @__PURE__ */ new Date(), source: options?.source, metadata: options?.metadata, correlationId: options?.correlationId ?? this.generateCorrelationId() }; this.addToHistory(event); super.emit(type, event); this.globalSubject.next(event); const subject = this.eventSubjects.get(type); if (subject) { subject.next(event); } return true; } /** * Listen to events (EventEmitter style) */ on(eventName, listener) { super.on(eventName, listener); return this; } /** * Listen to events once */ once(eventName, listener) { super.once(eventName, listener); return this; } /** * Remove event listener */ off(eventName, listener) { super.off(eventName, listener); return this; } /** * Create an observable stream for events matching pattern */ createStream(pattern) { return this.globalSubject.pipe( filter((event) => this.matchesPattern(event.type, pattern)) ); } /** * Create a typed observable stream */ createTypedStream(eventType) { let subject = this.eventSubjects.get(eventType); if (!subject) { subject = new Subject(); this.eventSubjects.set(eventType, subject); } return subject.asObservable(); } /** * Subscribe to events matching pattern */ subscribe(pattern, handler, _options) { const subscription = this.createStream(pattern).subscribe(handler); return { unsubscribe: () => subscription.unsubscribe() }; } /** * Get event history */ getHistory(filter2) { let events = this.eventHistory; if (filter2?.type) { events = events.filter((event) => this.matchesPattern(event.type, filter2.type)); } if (filter2?.since) { events = events.filter((event) => event.timestamp >= filter2.since); } if (filter2?.limit) { events = events.slice(-filter2.limit); } return events; } /** * Clear event history */ clearHistory() { this.eventHistory = []; } /** * Wait for specific event */ waitFor(eventType, timeout = 3e4) { return new Promise((resolve3, reject) => { const timer = setTimeout(() => { reject(new Error(`Timeout waiting for event: ${eventType}`)); }, timeout); this.once(eventType, (event) => { clearTimeout(timer); resolve3(event); }); }); } /** * Batch emit multiple events */ emitBatch(events) { events.forEach(({ type, data }) => { this.emit(type, data); }); } /** * Create event correlation for tracing */ correlate(correlationId) { return new EventCorrelator(this, correlationId); } /** * Add event to history */ addToHistory(event) { this.eventHistory.push(event); if (this.eventHistory.length > this.maxHistorySize) { this.eventHistory = this.eventHistory.slice(-this.maxHistorySize); } } /** * Check if event type matches pattern */ matchesPattern(eventType, pattern) { if (typeof pattern === "string") { if (pattern.includes("*")) { const regex = new RegExp("^" + pattern.replace(/\*/g, ".*") + "$"); return regex.test(eventType); } return eventType === pattern; } if (pattern instanceof RegExp) { return pattern.test(eventType); } if (typeof pattern === "function") { return pattern(eventType); } return false; } /** * Generate correlation ID */ generateCorrelationId() { return `corr_${Date.now()}_${++this.correlationIdCounter}`; } }; EventCorrelator = class { constructor(eventBus2, correlationId) { this.eventBus = eventBus2; this.correlationId = correlationId; } /** * Emit event with correlation ID */ emit(type, data, metadata) { this.eventBus.emit(type, data, { correlationId: this.correlationId, metadata }); } /** * Get all events for this correlation */ getCorrelatedEvents() { return this.eventBus.getHistory({ type: () => true }).filter((event) => event.correlationId === this.correlationId); } /** * Create stream for correlated events */ createStream() { return this.eventBus.createStream(() => true).pipe( filter((event) => event.correlationId === this.correlationId) ); } }; StreamManager = class { subjects = /* @__PURE__ */ new Map(); eventBus = EventBus.getInstance(); constructor() { this.eventBus.createStream("*").subscribe((event) => { this.updateStream(event.type, event.data); }); } /** * Create or get a data stream */ getStream(key, initialValue) { let subject = this.subjects.get(key); if (!subject) { subject = new BehaviorSubject(initialValue); this.subjects.set(key, subject); } return subject.asObservable(); } /** * Update stream with new data */ updateStream(key, data) { const subject = this.subjects.get(key); if (subject) { subject.next(data); } } /** * Create combined stream from multiple sources */ combineStreams(...keys) { const streams = keys.map((key) => this.getStream(key)); return merge(...streams).pipe( map(() => keys.map((key) => this.subjects.get(key)?.value)) ); } /** * Close stream */ closeStream(key) { const subject = this.subjects.get(key); if (subject) { subject.complete(); this.subjects.delete(key); } } /** * Close all streams */ closeAllStreams() { this.subjects.forEach((subject) => subject.complete()); this.subjects.clear(); } }; CommandResultStream = class { resultSubject = new Subject(); eventBus = EventBus.getInstance(); constructor() { this.eventBus.on("command:executed", (event) => { const data = event.data; this.resultSubject.next({ command: data.command, success: data.success, result: data.result, error: data.error, timestamp: event.timestamp }); }); } /** * Get stream of command results */ getResultStream() { return this.resultSubject.asObservable(); } /** * Get stream for specific command */ getCommandStream(command) { return this.resultSubject.pipe( filter((result) => result.command === command) ); } /** * Subscribe to command results */ subscribe(handler) { const subscription = this.resultSubject.subscribe(handler); return { unsubscribe: () => subscription.unsubscribe() }; } }; BlockchainEventListener = class { eventBus = EventBus.getInstance(); isListening = false; subscriptions = []; /** * Start listening to blockchain events */ async startListening() { if (this.isListening) { return; } this.isListening = true; this.simulateBlockchainEvents(); this.eventBus.emit("blockchain:listener:started"); } /** * Stop listening to blockchain events */ stopListening() { if (!this.isListening) { return; } this.isListening = false; this.subscriptions.forEach((sub) => sub.unsubscribe()); this.subscriptions = []; this.eventBus.emit("blockchain:listener:stopped"); } /** * Simulate blockchain events (replace with real implementation) */ simulateBlockchainEvents() { const events = [ "transaction:confirmed", "agent:registered", "escrow:created", "auction:bid_placed" ]; const emitRandomEvent = () => { if (!this.isListening) return; const eventType = events[Math.floor(Math.random() * events.length)]; this.eventBus.emit(`blockchain:${eventType}`, { blockHash: "block_" + Math.random().toString(36).substr(2, 9), timestamp: /* @__PURE__ */ new Date() }); setTimeout(emitRandomEvent, 5e3 + Math.random() * 1e4); }; setTimeout(emitRandomEvent, 1e3); } }; CLIStateManager = class { state = new BehaviorSubject({ activeCommand: null, user: null, network: "devnet", wallet: null, isOnline: true }); eventBus = EventBus.getInstance(); constructor() { this.setupEventHandlers(); } /** * Get current state */ getState() { return this.state.asObservable(); } /** * Update state */ updateState(updates) { const currentState = this.state.value; const newState = { ...currentState, ...updates }; this.state.next(newState); this.eventBus.emit("cli:state:updated", newState); } /** * Setup event handlers for state management */ setupEventHandlers() { this.eventBus.on("command:started", (event) => { const command = event.data.command; this.updateState({ activeCommand: command }); }); this.eventBus.on("command:completed", () => { this.updateState({ activeCommand: null }); }); this.eventBus.on("wallet:connected", (event) => { const wallet = event.data; this.updateState({ wallet }); }); this.eventBus.on("network:changed", (event) => { const network = event.data.network; this.updateState({ network }); }); } }; EventBus.getInstance(); new StreamManager(); new CommandResultStream(); new BlockchainEventListener(); new CLIStateManager(); } }); var PooledConnection, ConnectionPool, ConnectionPoolManager; var init_connection_pool = __esm({ "src/core/connection-pool.ts"() { init_event_system(); PooledConnection = class extends EventEmitter { rpc; endpoint; createdAt; lastUsed; requestCount = 0; isActive = false; pool; constructor(rpc, endpoint, pool) { super(); this.rpc = rpc; this.endpoint = endpoint; this.pool = pool; this.createdAt = /* @__PURE__ */ new Date(); this.lastUsed = /* @__PURE__ */ new Date(); } /** * Execute RPC call with performance tracking */ async call(method, params) { const startTime = Date.now(); this.isActive = true; this.requestCount++; this.lastUsed = /* @__PURE__ */ new Date(); try { const result = await this.rpc[method]?.(...params ?? []); const responseTime = Date.now() - startTime; this.updateResponseTime(responseTime); this.emit("request_completed", { method, responseTime, success: true }); return result; } catch (error) { const responseTime = Date.now() - startTime; this.updateResponseTime(responseTime); this.emit("request_failed", { method, responseTime, error }); throw error; } finally { this.isActive = false; this.pool.releaseConnection(this); } } /** * Get connection statistics */ getStats() { return { endpoint: this.endpoint.url, createdAt: this.createdAt, lastUsed: this.lastUsed, requestCount: this.requestCount, isActive: this.isActive, health: this.endpoint.health, responseTime: this.endpoint.responseTime }; } /** * Check if connection is stale */ isStale(maxIdleTime) { return Date.now() - this.lastUsed.getTime() > maxIdleTime; } /** * Update response time statistics */ updateResponseTime(responseTime) { const stats = this.endpoint.responseTime; stats.current = responseTime; stats.min = Math.min(stats.min, responseTime); stats.max = Math.max(stats.max, responseTime); stats.samples.push(responseTime); if (stats.samples.length > 100) { stats.samples = stats.samples.slice(-100); } stats.average = stats.samples.reduce((sum, time) => sum + time, 0) / stats.samples.length; } }; ConnectionPool = class extends EventEmitter { network; endpoints; connections = []; activeConnections = /* @__PURE__ */ new Set(); config; healthCheckInterval = null; stats = { totalConnections: 0, activeConnections: 0, idleConnections: 0, totalRequests: 0, averageResponseTime: 0, hitRate: 0, failures: 0, healthStats: {} }; constructor(network, endpoints, config2 = { minConnections: 2, maxConnections: 10, maxIdleTime: 3e5, // 5 minutes healthCheckInterval: 3e4 // 30 seconds }) { super(); this.network = network; this.endpoints = endpoints; this.config = config2; this.initializePool(); this.startHealthChecks(); } /** * Get connection from pool */ async getConnection() { const idleConnection = this.getIdleConnection(); if (idleConnection) { this.activeConnections.add(idleConnection); this.updateStats(); return idleConnection; } if (this.connections.length < this.config.maxConnections) { const connection = await this.createConnection(); this.connections.push(connection); this.activeConnections.add(connection); this.updateStats(); return connection; } return this.waitForConnection(); } /** * Release connection back to pool */ releaseConnection(connection) { this.activeConnections.delete(connection); this.updateStats(); this.emit("connection_released", connection); } /** * Get pool statistics */ getStats() { return { ...this.stats }; } /** * Close all connections and cleanup */ async close() { if (this.healthCheckInterval) { clearInterval(this.healthCheckInterval); this.healthCheckInterval = null; } for (const connection of this.connections) { connection.removeAllListeners(); } this.connections = []; this.activeConnections.clear(); this.emit("pool_closed"); } /** * Initialize pool with minimum connections */ async initializePool() { const promises = []; for (let i = 0; i < this.config.minConnections; i++) { promises.push(this.createConnection()); } try { const connections = await Promise.all(promises); this.connections.push(...connections); this.updateStats(); } catch (error) { this.emit("pool_initializationerror", error); } } /** * Create new connection */ async createConnection() { const endpoint = this.selectEndpoint(); try { const rpc = createSolanaRpc(endpoint.url); const connection = new PooledConnection(rpc, endpoint, this); connection.on("request_completed", (data) => { this.stats.totalRequests++; this.updateAverageResponseTime(data.responseTime); }); connection.on("request_failed", (_data) => { this.stats.failures++; this.updateEndpointHealth(endpoint, "degraded"); }); this.emit("connection_created", connection); return connection; } catch (error) { this.updateEndpointHealth(endpoint, "unhealthy"); throw new Error(`Failed to create connection to ${endpoint.url}: ${error}`); } } /** * Get idle connection from pool */ getIdleConnection() { for (const connection of this.connections) { if (!this.activeConnections.has(connection) && !connection.isStale(this.config.maxIdleTime)) { return connection; } } return null; } /** * Wait for connection to become available */ async waitForConnection() { return new Promise((resolve3, reject) => { const timeout = setTimeout(() => { reject(new Error("Connection timeout: No connections available")); }, 1e4); const onConnectionReleased = (connection) => { clearTimeout(timeout); this.off("connection_released", onConnectionReleased); this.activeConnections.add(connection); resolve3(connection); }; this.on("connection_released", onConnectionReleased); }); } /** * Select best endpoint using weighted round-robin */ selectEndpoint() { const healthyEndpoints = this.endpoints.filter( (ep) => ep.health === "healthy" || ep.health === "unknown" ); if (healthyEndpoints.length === 0) { const degradedEndpoints = this.endpoints.filter((ep) => ep.health === "degraded"); if (degradedEndpoints.length > 0) { return degradedEndpoints[0]; } return this.endpoints[0]; } const totalWeight = healthyEndpoints.reduce((sum, ep) => { const performanceWeight = Math.max(1, 1e3 / (ep.responseTime.average ?? 1e3)); return sum + ep.weight * performanceWeight; }, 0); const random = Math.random() * totalWeight; let currentWeight = 0; for (const endpoint of healthyEndpoints) { const performanceWeight = Math.max(1, 1e3 / (endpoint.responseTime.average ?? 1e3)); currentWeight += endpoint.weight * performanceWeight; if (random <= currentWeight) { return endpoint; } } return healthyEndpoints[0]; } /** * Start health check monitoring */ startHealthChecks() { this.healthCheckInterval = setInterval(() => { this.performHealthChecks(); }, this.config.healthCheckInterval); } /** * Perform health checks on all endpoints */ async performHealthChecks() { const promises = this.endpoints.map((endpoint) => this.checkEndpointHealth(endpoint)); await Promise.allSettled(promises); this.cleanupStaleConnections(); } /** * Check health of specific endpoint */ async checkEndpointHealth(endpoint) { const startTime = Date.now(); try { const rpc = createSolanaRpc(endpoint.url); await rpc.getLatestBlockhash?.(); const responseTime = Date.now() - startTime; endpoint.responseTime.current = responseTime; endpoint.lastHealthCheck = /* @__PURE__ */ new Date(); if (responseTime < 1e3) { this.updateEndpointHealth(endpoint, "healthy"); } else if (responseTime < 3e3) { this.updateEndpointHealth(endpoint, "degraded"); } else { this.updateEndpointHealth(endpoint, "unhealthy"); } } catch (error) { this.updateEndpointHealth(endpoint, "unhealthy"); endpoint.lastHealthCheck = /* @__PURE__ */ new Date(); } } /** * Update endpoint health status */ updateEndpointHealth(endpoint, health) { if (endpoint.health !== health) { const oldHealth = endpoint.health; endpoint.health = health; this.stats.healthStats[endpoint.url] = health; this.emit("endpoint_health_changed", { endpoint: endpoint.url, oldHealth, newHealth: health }); } } /** * Clean up stale connections */ cleanupStaleConnections() { const staleConnections = this.connections.filter( (conn) => !this.activeConnections.has(conn) && conn.isStale(this.config.maxIdleTime) ); for (const staleConnection of staleConnections) { const index = this.connections.indexOf(staleConnection); if (index > -1) { this.connections.splice(index, 1); staleConnection.removeAllListeners(); } } this.updateStats(); } /** * Update pool statistics */ updateStats() { this.stats.totalConnections = this.connections.length; this.stats.activeConnections = this.activeConnections.size; this.stats.idleConnections = this.connections.length - this.activeConnections.size; this.stats.hitRate = this.stats.totalRequests > 0 ? (this.stats.totalRequests - this.stats.failures) / this.stats.totalRequests * 100 : 0; for (const endpoint of this.endpoints) { this.stats.healthStats[endpoint.url] = endpoint.health; } } /** * Update average response time */ updateAverageResponseTime(responseTime) { const currentAvg = this.stats.averageResponseTime; const totalRequests = this.stats.totalRequests; this.stats.averageResponseTime = totalRequests > 1 ? (currentAvg * (totalRequests - 1) + responseTime) / totalRequests : responseTime; } }; ConnectionPoolManager = class _ConnectionPoolManager extends EventEmitter { static instance = null; pools = /* @__PURE__ */ new Map(); eventBus = EventBus.getInstance(); defaultEndpoints = { "mainnet-beta": [ { url: "https://api.mainnet-beta.solana.com", weight: 10, maxConnections: 5, timeout: 3e4, healthCheckInterval: 6e4, health: "unknown", lastHealthCheck: /* @__PURE__ */ new Date(), responseTime: { current: 0, average: 0, min: Infinity, max: 0, samples: [] } }, { url: "https://solana-api.projectserum.com", weight: 8, maxConnections: 3, timeout: 3e4, healthCheckInterval: 6e4, health: "unknown", lastHealthCheck: /* @__PURE__ */ new Date(), responseTime: { current: 0, average: 0, min: Infinity, max: 0, samples: [] } } ], "testnet": [ { url: "https://api.testnet.solana.com", weight: 10, maxConnections: 5, timeout: 3e4, healthCheckInterval: 6e4, health: "unknown", lastHealthCheck: /* @__PURE__ */ new Date(), responseTime: { current: 0, average: 0, min: Infinity, max: 0, samples: [] } } ], "devnet": [ { url: "https://api.devnet.solana.com", weight: 10, maxConnections: 5, timeout: 3e4, healthCheckInterval: 6e4, health: "unknown", lastHealthCheck: /* @__PURE__ */ new Date(), responseTime: { current: 0, average: 0, min: Infinity, max: 0, samples: [] } } ], "localnet": [ { url: "http://localhost:8899", weight: 10, maxConnections: 3, timeout: 1e4, healthCheckInterval: 3e4, health: "unknown", lastHealthCheck: /* @__PURE__ */ new Date(), responseTime: { current: 0, average: 0, min: Infinity, max: 0, samples: [] } } ] }; /** * Get singleton instance */ static getInstance() { if (!_ConnectionPoolManager.instance) { _ConnectionPoolManager.instance = new _ConnectionPoolManager(); } return _ConnectionPoolManager.instance; } /** * Get connection for network */ async getConnection(network) { let pool = this.pools.get(network); if (!pool) { pool = await this.createPool(network); this.pools.set(network, pool); } return pool.getConnection(); } /** * Add custom RPC endpoint */ addEndpoint(network, endpoint) { if (!this.defaultEndpoints[network]) { this.defaultEndpoints[network] = []; } this.defaultEndpoints[network].push(endpoint); const existingPool = this.pools.get(network); if (existingPool) { existingPool.close(); this.pools.delete(network); } this.eventBus.emit("connection_pool:endpoint_added", { network, endpoint }); } /** * Get statistics for all pools */ getAllStats() { const stats = {}; for (const [network, pool] of this.pools) { stats[network] = pool.getStats(); } return stats; } /** * Close all pools */ async closeAll() { const promises = Array.from(this.pools.values()).map((pool) => pool.close()); await Promise.all(promises); this.pools.clear(); this.eventBus.emit("connection_pool:all_closed"); } /** * Create pool for network */ async createPool(network) { const endpoints = this.defaultEndpoints[network] ?? []; if (endpoints.length === 0) { throw new Error(`No RP