@ghostspeak/cli
Version:
Command-line interface for GhostSpeak AI Agent Commerce Protocol - Production Ready Beta
1,525 lines (1,523 loc) • 566 kB
JavaScript
#!/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