From 2777fdb98774acc613f590d8c7ad1facda33076a Mon Sep 17 00:00:00 2001 From: aitbc Date: Thu, 30 Apr 2026 10:28:03 +0200 Subject: [PATCH] Remove backup and temporary files from git Deleted .bak, .backup, and .orig files: - 2 .orig files from blockchain-node - 9 .bak files from cli commands - 1 .bak file from dev scripts - 1 .backup file from docs - 1 .bak file from scripts These files add noise and should not be tracked in git. --- .../src/aitbc_chain/consensus/poa.py.orig | 229 ---- .../src/aitbc_chain/gossip/broker.py.orig | 307 ------ cli/aitbc_cli/commands/agent_comm.py.bak | 496 --------- cli/aitbc_cli/commands/analytics.py.bak | 402 ------- cli/aitbc_cli/commands/chain.py.bak | 562 ---------- cli/aitbc_cli/commands/cross_chain.py.bak | 476 --------- cli/aitbc_cli/commands/deployment.py.bak | 378 ------- cli/aitbc_cli/commands/exchange.py.bak | 981 ------------------ cli/aitbc_cli/commands/marketplace_cmd.py.bak | 494 --------- cli/aitbc_cli/commands/monitor.py.bak | 485 --------- cli/aitbc_cli/commands/node.py.bak | 439 -------- dev/scripts/testing/ws_load_test.py.bak | 54 - .../01_getting_started/3_cli.md.backup | 250 ----- scripts/dev/ws_load_test.py.bak | 54 - 14 files changed, 5607 deletions(-) delete mode 100644 apps/blockchain-node/src/aitbc_chain/consensus/poa.py.orig delete mode 100755 apps/blockchain-node/src/aitbc_chain/gossip/broker.py.orig delete mode 100755 cli/aitbc_cli/commands/agent_comm.py.bak delete mode 100755 cli/aitbc_cli/commands/analytics.py.bak delete mode 100755 cli/aitbc_cli/commands/chain.py.bak delete mode 100755 cli/aitbc_cli/commands/cross_chain.py.bak delete mode 100755 cli/aitbc_cli/commands/deployment.py.bak delete mode 100755 cli/aitbc_cli/commands/exchange.py.bak delete mode 100755 cli/aitbc_cli/commands/marketplace_cmd.py.bak delete mode 100755 cli/aitbc_cli/commands/monitor.py.bak delete mode 100755 cli/aitbc_cli/commands/node.py.bak delete mode 100755 dev/scripts/testing/ws_load_test.py.bak delete mode 100644 docs/beginner/01_getting_started/3_cli.md.backup delete mode 100644 scripts/dev/ws_load_test.py.bak diff --git a/apps/blockchain-node/src/aitbc_chain/consensus/poa.py.orig b/apps/blockchain-node/src/aitbc_chain/consensus/poa.py.orig deleted file mode 100644 index 3cb8261e..00000000 --- a/apps/blockchain-node/src/aitbc_chain/consensus/poa.py.orig +++ /dev/null @@ -1,229 +0,0 @@ -import asyncio -import hashlib -import re -from datetime import datetime -from typing import Callable, ContextManager, Optional - -from sqlmodel import Session, select - -from ..logger import get_logger -from ..metrics import metrics_registry -from ..config import ProposerConfig -from ..models import Block -from ..gossip import gossip_broker - -_METRIC_KEY_SANITIZE = re.compile(r"[^a-zA-Z0-9_]") - - -def _sanitize_metric_suffix(value: str) -> str: - sanitized = _METRIC_KEY_SANITIZE.sub("_", value).strip("_") - return sanitized or "unknown" - - - -import time - -class CircuitBreaker: - def __init__(self, threshold: int, timeout: int): - self._threshold = threshold - self._timeout = timeout - self._failures = 0 - self._last_failure_time = 0.0 - self._state = "closed" - - @property - def state(self) -> str: - if self._state == "open": - if time.time() - self._last_failure_time > self._timeout: - self._state = "half-open" - return self._state - - def allow_request(self) -> bool: - state = self.state - if state == "closed": - return True - if state == "half-open": - return True - return False - - def record_failure(self) -> None: - self._failures += 1 - self._last_failure_time = time.time() - if self._failures >= self._threshold: - self._state = "open" - - def record_success(self) -> None: - self._failures = 0 - self._state = "closed" - -class PoAProposer: - """Proof-of-Authority block proposer. - - Responsible for periodically proposing blocks if this node is configured as a proposer. - In the real implementation, this would involve checking the mempool, validating transactions, - and signing the block. - """ - - def __init__( - self, - *, - config: ProposerConfig, - session_factory: Callable[[], ContextManager[Session]], - ) -> None: - self._config = config - self._session_factory = session_factory - self._logger = get_logger(__name__) - self._stop_event = asyncio.Event() - self._task: Optional[asyncio.Task[None]] = None - self._last_proposer_id: Optional[str] = None - - async def start(self) -> None: - if self._task is not None: - return - self._logger.info("Starting PoA proposer loop", extra={"interval": self._config.interval_seconds}) - self._ensure_genesis_block() - self._stop_event.clear() - self._task = asyncio.create_task(self._run_loop()) - - async def stop(self) -> None: - if self._task is None: - return - self._logger.info("Stopping PoA proposer loop") - self._stop_event.set() - await self._task - self._task = None - - async def _run_loop(self) -> None: - while not self._stop_event.is_set(): - await self._wait_until_next_slot() - if self._stop_event.is_set(): - break - try: - self._propose_block() - except Exception as exc: # pragma: no cover - defensive logging - self._logger.exception("Failed to propose block", extra={"error": str(exc)}) - - async def _wait_until_next_slot(self) -> None: - head = self._fetch_chain_head() - if head is None: - return - now = datetime.utcnow() - elapsed = (now - head.timestamp).total_seconds() - sleep_for = max(self._config.interval_seconds - elapsed, 0.1) - if sleep_for <= 0: - sleep_for = 0.1 - try: - await asyncio.wait_for(self._stop_event.wait(), timeout=sleep_for) - except asyncio.TimeoutError: - return - - async def _propose_block(self) -> None: - # Check internal mempool - from ..mempool import get_mempool - if get_mempool().size(self._config.chain_id) == 0: - return - - with self._session_factory() as session: - head = session.exec(select(Block).where(Block.chain_id == self._config.chain_id).order_by(Block.height.desc()).limit(1)).first() - next_height = 0 - parent_hash = "0x00" - interval_seconds: Optional[float] = None - if head is not None: - next_height = head.height + 1 - parent_hash = head.hash - interval_seconds = (datetime.utcnow() - head.timestamp).total_seconds() - - timestamp = datetime.utcnow() - block_hash = self._compute_block_hash(next_height, parent_hash, timestamp) - - block = Block( - chain_id=self._config.chain_id, - height=next_height, - hash=block_hash, - parent_hash=parent_hash, - proposer=self._config.proposer_id, - timestamp=timestamp, - tx_count=0, - state_root=None, - ) - session.add(block) - session.commit() - - metrics_registry.increment("blocks_proposed_total") - metrics_registry.set_gauge("chain_head_height", float(next_height)) - if interval_seconds is not None and interval_seconds >= 0: - metrics_registry.observe("block_interval_seconds", interval_seconds) - metrics_registry.set_gauge("poa_last_block_interval_seconds", float(interval_seconds)) - - proposer_suffix = _sanitize_metric_suffix(self._config.proposer_id) - metrics_registry.increment(f"poa_blocks_proposed_total_{proposer_suffix}") - if self._last_proposer_id is not None and self._last_proposer_id != self._config.proposer_id: - metrics_registry.increment("poa_proposer_switches_total") - self._last_proposer_id = self._config.proposer_id - - self._logger.info( - "Proposed block", - extra={ - "height": block.height, - "hash": block.hash, - "proposer": block.proposer, - }, - ) - - # Broadcast the new block - await gossip_broker.publish( - "blocks", - { - "height": block.height, - "hash": block.hash, - "parent_hash": block.parent_hash, - "proposer": block.proposer, - "timestamp": block.timestamp.isoformat(), - "tx_count": block.tx_count, - "state_root": block.state_root, - } - ) - - async def _ensure_genesis_block(self) -> None: - with self._session_factory() as session: - head = session.exec(select(Block).where(Block.chain_id == self._config.chain_id).order_by(Block.height.desc()).limit(1)).first() - if head is not None: - return - - # Use a deterministic genesis timestamp so all nodes agree on the genesis block hash - timestamp = datetime(2025, 1, 1, 0, 0, 0) - block_hash = self._compute_block_hash(0, "0x00", timestamp) - genesis = Block( - chain_id=self._config.chain_id, - height=0, - hash=block_hash, - parent_hash="0x00", - proposer="genesis", - timestamp=timestamp, - tx_count=0, - state_root=None, - ) - session.add(genesis) - session.commit() - - # Broadcast genesis block for initial sync - await gossip_broker.publish( - "blocks", - { - "height": genesis.height, - "hash": genesis.hash, - "parent_hash": genesis.parent_hash, - "proposer": genesis.proposer, - "timestamp": genesis.timestamp.isoformat(), - "tx_count": genesis.tx_count, - "state_root": genesis.state_root, - } - ) - - def _fetch_chain_head(self) -> Optional[Block]: - with self._session_factory() as session: - return session.exec(select(Block).order_by(Block.height.desc()).limit(1)).first() - - def _compute_block_hash(self, height: int, parent_hash: str, timestamp: datetime) -> str: - payload = f"{self._config.chain_id}|{height}|{parent_hash}|{timestamp.isoformat()}".encode() - return "0x" + hashlib.sha256(payload).hexdigest() diff --git a/apps/blockchain-node/src/aitbc_chain/gossip/broker.py.orig b/apps/blockchain-node/src/aitbc_chain/gossip/broker.py.orig deleted file mode 100755 index f7ae9adb..00000000 --- a/apps/blockchain-node/src/aitbc_chain/gossip/broker.py.orig +++ /dev/null @@ -1,307 +0,0 @@ -from __future__ import annotations - -import asyncio -import json -import warnings -from collections import defaultdict -from contextlib import suppress -from dataclasses import dataclass -from typing import Any, Callable, Dict, List, Optional, Set - -warnings.filterwarnings("ignore", message="coroutine.* was never awaited", category=RuntimeWarning) - -try: - from broadcaster import Broadcast -except ImportError: # pragma: no cover - Broadcast = None # type: ignore[assignment] - -from ..metrics import metrics_registry - - -def _increment_publication(metric_prefix: str, topic: str) -> None: - metrics_registry.increment(f"{metric_prefix}_total") - metrics_registry.increment(f"{metric_prefix}_topic_{topic}") - - -def _set_queue_gauge(topic: str, size: int) -> None: - metrics_registry.set_gauge(f"gossip_queue_size_{topic}", float(size)) - - -def _update_subscriber_metrics(topics: Dict[str, List["asyncio.Queue[Any]"]]) -> None: - for topic, queues in topics.items(): - metrics_registry.set_gauge(f"gossip_subscribers_topic_{topic}", float(len(queues))) - total = sum(len(queues) for queues in topics.values()) - metrics_registry.set_gauge("gossip_subscribers_total", float(total)) - - -def _clear_topic_metrics(topic: str) -> None: - metrics_registry.set_gauge(f"gossip_subscribers_topic_{topic}", 0.0) - _set_queue_gauge(topic, 0) - -@dataclass -class TopicSubscription: - topic: str - queue: "asyncio.Queue[Any]" - _unsubscribe: Callable[[], None] - - def close(self) -> None: - self._unsubscribe() - - async def get(self) -> Any: - return await self.queue.get() - - async def __aiter__(self): # type: ignore[override] - try: - while True: - yield await self.queue.get() - finally: - self.close() - - -class GossipBackend: - async def start(self) -> None: # pragma: no cover - overridden as needed - return None - - async def publish(self, topic: str, message: Any) -> None: - raise NotImplementedError - - async def subscribe(self, topic: str, max_queue_size: int = 100) -> TopicSubscription: - raise NotImplementedError - - async def shutdown(self) -> None: - return None - - -class InMemoryGossipBackend(GossipBackend): - def __init__(self) -> None: - self._topics: Dict[str, List["asyncio.Queue[Any]"]] = defaultdict(list) - self._lock = asyncio.Lock() - - async def publish(self, topic: str, message: Any) -> None: - async with self._lock: - queues = list(self._topics.get(topic, [])) - for queue in queues: - await queue.put(message) - _set_queue_gauge(topic, queue.qsize()) - _increment_publication("gossip_publications", topic) - - async def subscribe(self, topic: str, max_queue_size: int = 100) -> TopicSubscription: - queue: "asyncio.Queue[Any]" = asyncio.Queue(maxsize=max_queue_size) - - async with self._lock: - self._topics[topic].append(queue) - _update_subscriber_metrics(self._topics) - - _set_queue_gauge(topic, queue.qsize()) - - def _unsubscribe() -> None: - async def _remove() -> None: - async with self._lock: - queues = self._topics.get(topic) - if queues is None: - return - if queue in queues: - queues.remove(queue) - if not queues: - self._topics.pop(topic, None) - _clear_topic_metrics(topic) - _update_subscriber_metrics(self._topics) - - asyncio.create_task(_remove()) - - return TopicSubscription(topic=topic, queue=queue, _unsubscribe=_unsubscribe) - - async def shutdown(self) -> None: - async with self._lock: - topics = list(self._topics.keys()) - self._topics.clear() - for topic in topics: - _clear_topic_metrics(topic) - _update_subscriber_metrics(self._topics) - - -class BroadcastGossipBackend(GossipBackend): - def __init__(self, url: str) -> None: - if Broadcast is None: # provide in-process fallback when Broadcast is missing - self._broadcast = _InProcessBroadcast() - else: - self._broadcast = Broadcast(url) # type: ignore[arg-type] - self._tasks: Set[asyncio.Task[None]] = set() - self._lock = asyncio.Lock() - self._running = False - - async def start(self) -> None: - if not self._running: - await self._broadcast.connect() # type: ignore[union-attr] - self._running = True - - async def publish(self, topic: str, message: Any) -> None: - if not self._running: - raise RuntimeError("Broadcast backend not started") - payload = _encode_message(message) - await self._broadcast.publish(topic, payload) # type: ignore[union-attr] - _increment_publication("gossip_broadcast_publications", topic) - - async def subscribe(self, topic: str, max_queue_size: int = 100) -> TopicSubscription: - if not self._running: - raise RuntimeError("Broadcast backend not started") - - queue: "asyncio.Queue[Any]" = asyncio.Queue(maxsize=max_queue_size) - stop_event = asyncio.Event() - - async def _run_subscription() -> None: - async with self._broadcast.subscribe(topic) as subscriber: # type: ignore[attr-defined,union-attr] - async for event in subscriber: # type: ignore[union-attr] - if stop_event.is_set(): - break - data = _decode_message(getattr(event, "message", event)) - try: - await queue.put(data) - _set_queue_gauge(topic, queue.qsize()) - except asyncio.CancelledError: - break - - task = asyncio.create_task(_run_subscription(), name=f"broadcast-sub:{topic}") - async with self._lock: - self._tasks.add(task) - metrics_registry.set_gauge("gossip_broadcast_subscribers_total", float(len(self._tasks))) - - def _unsubscribe() -> None: - async def _stop() -> None: - stop_event.set() - task.cancel() - with suppress(asyncio.CancelledError): - await task - async with self._lock: - self._tasks.discard(task) - metrics_registry.set_gauge("gossip_broadcast_subscribers_total", float(len(self._tasks))) - - asyncio.create_task(_stop()) - - return TopicSubscription(topic=topic, queue=queue, _unsubscribe=_unsubscribe) - - async def shutdown(self) -> None: - async with self._lock: - tasks = list(self._tasks) - self._tasks.clear() - metrics_registry.set_gauge("gossip_broadcast_subscribers_total", 0.0) - for task in tasks: - task.cancel() - with suppress(asyncio.CancelledError): - await task - if self._running: - await self._broadcast.disconnect() # type: ignore[union-attr] - self._running = False - - -class GossipBroker: - def __init__(self, backend: GossipBackend) -> None: - self._backend = backend - self._lock = asyncio.Lock() - self._started = False - - async def publish(self, topic: str, message: Any) -> None: - if not self._started: - await self._backend.start() - self._started = True - await self._backend.publish(topic, message) - - async def subscribe(self, topic: str, max_queue_size: int = 100) -> TopicSubscription: - if not self._started: - await self._backend.start() - self._started = True - return await self._backend.subscribe(topic, max_queue_size=max_queue_size) - - async def set_backend(self, backend: GossipBackend) -> None: - await backend.start() - async with self._lock: - previous = self._backend - self._backend = backend - self._started = True - await previous.shutdown() - - async def shutdown(self) -> None: - await self._backend.shutdown() - - -class _InProcessSubscriber: - def __init__(self, queue: "asyncio.Queue[Any]"): - self._queue = queue - - def __aiter__(self): # type: ignore[override] - return self._iterator() - - async def _iterator(self): - while True: - yield await self._queue.get() - - -class _InProcessBroadcast: - """Minimal in-memory broadcast substitute for tests when Starlette Broadcast is absent.""" - - def __init__(self) -> None: - self._topics: Dict[str, List["asyncio.Queue[Any]"]] = defaultdict(list) - self._lock = asyncio.Lock() - self._running = False - - async def connect(self) -> None: - self._running = True - - async def disconnect(self) -> None: - async with self._lock: - self._topics.clear() - self._running = False - - @asynccontextmanager - async def subscribe(self, topic: str): - queue: "asyncio.Queue[Any]" = asyncio.Queue() - async with self._lock: - self._topics[topic].append(queue) - - try: - yield _InProcessSubscriber(queue) - finally: - async with self._lock: - queues = self._topics.get(topic) - if queues and queue in queues: - queues.remove(queue) - - async def publish(self, topic: str, message: Any) -> None: - if not self._running: - raise RuntimeError("Broadcast backend not started") - async with self._lock: - queues = list(self._topics.get(topic, [])) - for queue in queues: - await queue.put(message) - - -def create_backend(backend_type: str, *, broadcast_url: Optional[str] = None) -> GossipBackend: - backend = backend_type.lower() - if backend in {"memory", "inmemory", "local"}: - return InMemoryGossipBackend() - if backend in {"broadcast", "starlette", "redis"}: - if not broadcast_url: - raise ValueError("Broadcast backend requires a gossip_broadcast_url setting") - return BroadcastGossipBackend(broadcast_url) - raise ValueError(f"Unsupported gossip backend '{backend_type}'") - - -def _encode_message(message: Any) -> Any: - if isinstance(message, (str, bytes, bytearray)): - return message - return json.dumps(message, separators=(",", ":")) - - -def _decode_message(message: Any) -> Any: - if isinstance(message, (bytes, bytearray)): - message = message.decode("utf-8") - if isinstance(message, str): - try: - return json.loads(message) - except json.JSONDecodeError: - return message - return message - - -gossip_broker = GossipBroker(InMemoryGossipBackend()) - diff --git a/cli/aitbc_cli/commands/agent_comm.py.bak b/cli/aitbc_cli/commands/agent_comm.py.bak deleted file mode 100755 index 79f37e09..00000000 --- a/cli/aitbc_cli/commands/agent_comm.py.bak +++ /dev/null @@ -1,496 +0,0 @@ -"""Cross-chain agent communication commands for AITBC CLI""" - -import click -import asyncio -import json -from datetime import datetime, timedelta -from typing import Optional -from ..core.config import load_multichain_config -from ..core.agent_communication import ( - CrossChainAgentCommunication, AgentInfo, AgentMessage, - MessageType, AgentStatus -) -from ..utils import output, error, success - -@click.group() -def agent_comm(): - """Cross-chain agent communication commands""" - pass - -@agent_comm.command() -@click.argument('agent_id') -@click.argument('name') -@click.argument('chain_id') -@click.argument('endpoint') -@click.option('--capabilities', help='Comma-separated list of capabilities') -@click.option('--reputation', default=0.5, help='Initial reputation score') -@click.option('--version', default='1.0.0', help='Agent version') -@click.pass_context -def register(ctx, agent_id, name, chain_id, endpoint, capabilities, reputation, version): - """Register an agent in the cross-chain network""" - try: - config = load_multichain_config() - comm = CrossChainAgentCommunication(config) - - # Parse capabilities - cap_list = capabilities.split(',') if capabilities else [] - - # Create agent info - agent_info = AgentInfo( - agent_id=agent_id, - name=name, - chain_id=chain_id, - node_id="default-node", # Would be determined dynamically - status=AgentStatus.ACTIVE, - capabilities=cap_list, - reputation_score=reputation, - last_seen=datetime.now(), - endpoint=endpoint, - version=version - ) - - # Register agent - success = asyncio.run(comm.register_agent(agent_info)) - - if success: - success(f"Agent {agent_id} registered successfully!") - - agent_data = { - "Agent ID": agent_id, - "Name": name, - "Chain ID": chain_id, - "Status": "active", - "Capabilities": ", ".join(cap_list), - "Reputation": f"{reputation:.2f}", - "Endpoint": endpoint, - "Version": version - } - - output(agent_data, ctx.obj.get('output_format', 'table')) - else: - error(f"Failed to register agent {agent_id}") - raise click.Abort() - - except Exception as e: - error(f"Error registering agent: {str(e)}") - raise click.Abort() - -@agent_comm.command() -@click.option('--chain-id', help='Filter by chain ID') -@click.option('--status', type=click.Choice(['active', 'inactive', 'busy', 'offline']), help='Filter by status') -@click.option('--capabilities', help='Filter by capabilities (comma-separated)') -@click.option('--format', type=click.Choice(['table', 'json']), default='table', help='Output format') -@click.pass_context -def list(ctx, chain_id, status, capabilities, format): - """List registered agents""" - try: - config = load_multichain_config() - comm = CrossChainAgentCommunication(config) - - # Get all agents - agents = list(comm.agents.values()) - - # Apply filters - if chain_id: - agents = [a for a in agents if a.chain_id == chain_id] - - if status: - agents = [a for a in agents if a.status.value == status] - - if capabilities: - required_caps = [cap.strip() for cap in capabilities.split(',')] - agents = [a for a in agents if any(cap in a.capabilities for cap in required_caps)] - - if not agents: - output("No agents found", ctx.obj.get('output_format', 'table')) - return - - # Format output - agent_data = [ - { - "Agent ID": agent.agent_id, - "Name": agent.name, - "Chain ID": agent.chain_id, - "Status": agent.status.value, - "Reputation": f"{agent.reputation_score:.2f}", - "Capabilities": ", ".join(agent.capabilities[:3]), # Show first 3 - "Last Seen": agent.last_seen.strftime("%Y-%m-%d %H:%M:%S") - } - for agent in agents - ] - - output(agent_data, ctx.obj.get('output_format', format), title="Registered Agents") - - except Exception as e: - error(f"Error listing agents: {str(e)}") - raise click.Abort() - -@agent_comm.command() -@click.argument('chain_id') -@click.option('--capabilities', help='Required capabilities (comma-separated)') -@click.option('--format', type=click.Choice(['table', 'json']), default='table', help='Output format') -@click.pass_context -def discover(ctx, chain_id, capabilities, format): - """Discover agents on a specific chain""" - try: - config = load_multichain_config() - comm = CrossChainAgentCommunication(config) - - # Parse capabilities - cap_list = capabilities.split(',') if capabilities else None - - # Discover agents - agents = asyncio.run(comm.discover_agents(chain_id, cap_list)) - - if not agents: - output(f"No agents found on chain {chain_id}", ctx.obj.get('output_format', 'table')) - return - - # Format output - agent_data = [ - { - "Agent ID": agent.agent_id, - "Name": agent.name, - "Status": agent.status.value, - "Reputation": f"{agent.reputation_score:.2f}", - "Capabilities": ", ".join(agent.capabilities), - "Endpoint": agent.endpoint, - "Version": agent.version - } - for agent in agents - ] - - output(agent_data, ctx.obj.get('output_format', format), title=f"Agents on Chain {chain_id}") - - except Exception as e: - error(f"Error discovering agents: {str(e)}") - raise click.Abort() - -@agent_comm.command() -@click.argument('sender_id') -@click.argument('receiver_id') -@click.argument('message_type') -@click.argument('chain_id') -@click.option('--payload', help='Message payload (JSON string)') -@click.option('--target-chain', help='Target chain for cross-chain messages') -@click.option('--priority', default=5, help='Message priority (1-10)') -@click.option('--ttl', default=3600, help='Time to live in seconds') -@click.pass_context -def send(ctx, sender_id, receiver_id, message_type, chain_id, payload, target_chain, priority, ttl): - """Send a message to an agent""" - try: - config = load_multichain_config() - comm = CrossChainAgentCommunication(config) - - # Parse message type - try: - msg_type = MessageType(message_type) - except ValueError: - error(f"Invalid message type: {message_type}") - error(f"Valid types: {[t.value for t in MessageType]}") - raise click.Abort() - - # Parse payload - payload_dict = {} - if payload: - try: - payload_dict = json.loads(payload) - except json.JSONDecodeError: - error("Invalid JSON payload") - raise click.Abort() - - # Create message - message = AgentMessage( - message_id=f"msg_{datetime.now().strftime('%Y%m%d%H%M%S')}_{sender_id}", - sender_id=sender_id, - receiver_id=receiver_id, - message_type=msg_type, - chain_id=chain_id, - target_chain_id=target_chain, - payload=payload_dict, - timestamp=datetime.now(), - signature="auto_generated", # Would be cryptographically signed - priority=priority, - ttl_seconds=ttl - ) - - # Send message - success = asyncio.run(comm.send_message(message)) - - if success: - success(f"Message sent successfully to {receiver_id}") - - message_data = { - "Message ID": message.message_id, - "Sender": sender_id, - "Receiver": receiver_id, - "Type": message_type, - "Chain": chain_id, - "Target Chain": target_chain or "Same", - "Priority": priority, - "TTL": f"{ttl}s", - "Sent": message.timestamp.strftime("%Y-%m-%d %H:%M:%S") - } - - output(message_data, ctx.obj.get('output_format', 'table')) - else: - error(f"Failed to send message to {receiver_id}") - raise click.Abort() - - except Exception as e: - error(f"Error sending message: {str(e)}") - raise click.Abort() - -@agent_comm.command() -@click.argument('agent_ids', nargs=-1, required=True) -@click.argument('collaboration_type') -@click.option('--governance', help='Governance rules (JSON string)') -@click.pass_context -def collaborate(ctx, agent_ids, collaboration_type, governance): - """Create a multi-agent collaboration""" - try: - config = load_multichain_config() - comm = CrossChainAgentCommunication(config) - - # Parse governance rules - governance_dict = {} - if governance: - try: - governance_dict = json.loads(governance) - except json.JSONDecodeError: - error("Invalid JSON governance rules") - raise click.Abort() - - # Create collaboration - collaboration_id = asyncio.run(comm.create_collaboration( - list(agent_ids), collaboration_type, governance_dict - )) - - if collaboration_id: - success(f"Collaboration created: {collaboration_id}") - - collab_data = { - "Collaboration ID": collaboration_id, - "Type": collaboration_type, - "Participants": ", ".join(agent_ids), - "Status": "active", - "Created": datetime.now().strftime("%Y-%m-%d %H:%M:%S") - } - - output(collab_data, ctx.obj.get('output_format', 'table')) - else: - error("Failed to create collaboration") - raise click.Abort() - - except Exception as e: - error(f"Error creating collaboration: {str(e)}") - raise click.Abort() - -@agent_comm.command() -@click.argument('agent_id') -@click.argument('interaction_result', type=click.Choice(['success', 'failure'])) -@click.option('--feedback', type=float, help='Feedback score (0.0-1.0)') -@click.pass_context -def reputation(ctx, agent_id, interaction_result, feedback): - """Update agent reputation""" - try: - config = load_multichain_config() - comm = CrossChainAgentCommunication(config) - - # Update reputation - success = asyncio.run(comm.update_reputation( - agent_id, interaction_result == 'success', feedback - )) - - if success: - # Get updated reputation - agent_status = asyncio.run(comm.get_agent_status(agent_id)) - - if agent_status and agent_status.get('reputation'): - rep = agent_status['reputation'] - success(f"Reputation updated for {agent_id}") - - rep_data = { - "Agent ID": agent_id, - "Reputation Score": f"{rep['reputation_score']:.3f}", - "Total Interactions": rep['total_interactions'], - "Successful": rep['successful_interactions'], - "Failed": rep['failed_interactions'], - "Success Rate": f"{(rep['successful_interactions'] / rep['total_interactions'] * 100):.1f}%" if rep['total_interactions'] > 0 else "N/A", - "Last Updated": rep['last_updated'] - } - - output(rep_data, ctx.obj.get('output_format', 'table')) - else: - success(f"Reputation updated for {agent_id}") - else: - error(f"Failed to update reputation for {agent_id}") - raise click.Abort() - - except Exception as e: - error(f"Error updating reputation: {str(e)}") - raise click.Abort() - -@agent_comm.command() -@click.argument('agent_id') -@click.option('--format', type=click.Choice(['table', 'json']), default='table', help='Output format') -@click.pass_context -def status(ctx, agent_id, format): - """Get detailed agent status""" - try: - config = load_multichain_config() - comm = CrossChainAgentCommunication(config) - - # Get agent status - agent_status = asyncio.run(comm.get_agent_status(agent_id)) - - if not agent_status: - error(f"Agent {agent_id} not found") - raise click.Abort() - - # Format output - status_data = [ - {"Metric": "Agent ID", "Value": agent_status["agent_info"]["agent_id"]}, - {"Metric": "Name", "Value": agent_status["agent_info"]["name"]}, - {"Metric": "Chain ID", "Value": agent_status["agent_info"]["chain_id"]}, - {"Metric": "Status", "Value": agent_status["status"]}, - {"Metric": "Reputation", "Value": f"{agent_status['agent_info']['reputation_score']:.3f}" if agent_status.get('reputation') else "N/A"}, - {"Metric": "Capabilities", "Value": ", ".join(agent_status["agent_info"]["capabilities"])}, - {"Metric": "Message Queue Size", "Value": agent_status["message_queue_size"]}, - {"Metric": "Active Collaborations", "Value": agent_status["active_collaborations"]}, - {"Metric": "Last Seen", "Value": agent_status["last_seen"]}, - {"Metric": "Endpoint", "Value": agent_status["agent_info"]["endpoint"]}, - {"Metric": "Version", "Value": agent_status["agent_info"]["version"]} - ] - - output(status_data, ctx.obj.get('output_format', format), title=f"Agent Status: {agent_id}") - - except Exception as e: - error(f"Error getting agent status: {str(e)}") - raise click.Abort() - -@agent_comm.command() -@click.option('--format', type=click.Choice(['table', 'json']), default='table', help='Output format') -@click.pass_context -def network(ctx, format): - """Get cross-chain network overview""" - try: - config = load_multichain_config() - comm = CrossChainAgentCommunication(config) - - # Get network overview - overview = asyncio.run(comm.get_network_overview()) - - if not overview: - error("No network data available") - raise click.Abort() - - # Overview data - overview_data = [ - {"Metric": "Total Agents", "Value": overview["total_agents"]}, - {"Metric": "Active Agents", "Value": overview["active_agents"]}, - {"Metric": "Total Collaborations", "Value": overview["total_collaborations"]}, - {"Metric": "Active Collaborations", "Value": overview["active_collaborations"]}, - {"Metric": "Total Messages", "Value": overview["total_messages"]}, - {"Metric": "Queued Messages", "Value": overview["queued_messages"]}, - {"Metric": "Average Reputation", "Value": f"{overview['average_reputation']:.3f}"}, - {"Metric": "Routing Table Size", "Value": overview["routing_table_size"]}, - {"Metric": "Discovery Cache Size", "Value": overview["discovery_cache_size"]} - ] - - output(overview_data, ctx.obj.get('output_format', format), title="Network Overview") - - # Agents by chain - if overview["agents_by_chain"]: - chain_data = [ - {"Chain ID": chain_id, "Total Agents": count, "Active Agents": overview["active_agents_by_chain"].get(chain_id, 0)} - for chain_id, count in overview["agents_by_chain"].items() - ] - - output(chain_data, ctx.obj.get('output_format', format), title="Agents by Chain") - - # Collaborations by type - if overview["collaborations_by_type"]: - collab_data = [ - {"Type": collab_type, "Count": count} - for collab_type, count in overview["collaborations_by_type"].items() - ] - - output(collab_data, ctx.obj.get('output_format', format), title="Collaborations by Type") - - except Exception as e: - error(f"Error getting network overview: {str(e)}") - raise click.Abort() - -@agent_comm.command() -@click.option('--realtime', is_flag=True, help='Real-time monitoring') -@click.option('--interval', default=10, help='Update interval in seconds') -@click.pass_context -def monitor(ctx, realtime, interval): - """Monitor cross-chain agent communication""" - try: - config = load_multichain_config() - comm = CrossChainAgentCommunication(config) - - if realtime: - # Real-time monitoring - from rich.console import Console - from rich.live import Live - from rich.table import Table - import time - - console = Console() - - def generate_monitor_table(): - try: - overview = asyncio.run(comm.get_network_overview()) - - table = Table(title=f"Agent Network Monitor - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") - table.add_column("Metric", style="cyan") - table.add_column("Value", style="green") - - table.add_row("Total Agents", str(overview["total_agents"])) - table.add_row("Active Agents", str(overview["active_agents"])) - table.add_row("Active Collaborations", str(overview["active_collaborations"])) - table.add_row("Queued Messages", str(overview["queued_messages"])) - table.add_row("Avg Reputation", f"{overview['average_reputation']:.3f}") - - # Add top chains by agent count - if overview["agents_by_chain"]: - table.add_row("", "") - table.add_row("Top Chains by Agents", "") - for chain_id, count in sorted(overview["agents_by_chain"].items(), key=lambda x: x[1], reverse=True)[:3]: - active = overview["active_agents_by_chain"].get(chain_id, 0) - table.add_row(f" {chain_id}", f"{count} total, {active} active") - - return table - except Exception as e: - return f"Error getting network data: {e}" - - with Live(generate_monitor_table(), refresh_per_second=1) as live: - try: - while True: - live.update(generate_monitor_table()) - time.sleep(interval) - except KeyboardInterrupt: - console.print("\n[yellow]Monitoring stopped by user[/yellow]") - else: - # Single snapshot - overview = asyncio.run(comm.get_network_overview()) - - monitor_data = [ - {"Metric": "Total Agents", "Value": overview["total_agents"]}, - {"Metric": "Active Agents", "Value": overview["active_agents"]}, - {"Metric": "Total Collaborations", "Value": overview["total_collaborations"]}, - {"Metric": "Active Collaborations", "Value": overview["active_collaborations"]}, - {"Metric": "Total Messages", "Value": overview["total_messages"]}, - {"Metric": "Queued Messages", "Value": overview["queued_messages"]}, - {"Metric": "Average Reputation", "Value": f"{overview['average_reputation']:.3f}"}, - {"Metric": "Routing Table Size", "Value": overview["routing_table_size"]} - ] - - output(monitor_data, ctx.obj.get('output_format', 'table'), title="Agent Network Monitor") - - except Exception as e: - error(f"Error during monitoring: {str(e)}") - raise click.Abort() diff --git a/cli/aitbc_cli/commands/analytics.py.bak b/cli/aitbc_cli/commands/analytics.py.bak deleted file mode 100755 index 64d6d8ac..00000000 --- a/cli/aitbc_cli/commands/analytics.py.bak +++ /dev/null @@ -1,402 +0,0 @@ -"""Analytics and monitoring commands for AITBC CLI""" - -import click -import asyncio -from datetime import datetime, timedelta -from typing import Optional -from ..core.config import load_multichain_config -from ..core.analytics import ChainAnalytics -from ..utils import output, error, success - -@click.group() -def analytics(): - """Chain analytics and monitoring commands""" - pass - -@analytics.command() -@click.option('--chain-id', help='Specific chain ID to analyze') -@click.option('--hours', default=24, help='Time range in hours') -@click.option('--format', type=click.Choice(['table', 'json']), default='table', help='Output format') -@click.pass_context -def summary(ctx, chain_id, hours, format): - """Get performance summary for chains""" - try: - config = load_multichain_config() - analytics = ChainAnalytics(config) - - if chain_id: - # Single chain summary - summary = analytics.get_chain_performance_summary(chain_id, hours) - if not summary: - error(f"No data available for chain {chain_id}") - raise click.Abort() - - # Format summary for display - summary_data = [ - {"Metric": "Chain ID", "Value": summary["chain_id"]}, - {"Metric": "Time Range", "Value": f"{summary['time_range_hours']} hours"}, - {"Metric": "Data Points", "Value": summary["data_points"]}, - {"Metric": "Health Score", "Value": f"{summary['health_score']:.1f}/100"}, - {"Metric": "Active Alerts", "Value": summary["active_alerts"]}, - {"Metric": "Avg TPS", "Value": f"{summary['statistics']['tps']['avg']:.2f}"}, - {"Metric": "Avg Block Time", "Value": f"{summary['statistics']['block_time']['avg']:.2f}s"}, - {"Metric": "Avg Gas Price", "Value": f"{summary['statistics']['gas_price']['avg']:,} wei"} - ] - - output(summary_data, ctx.obj.get('output_format', format), title=f"Chain Summary: {chain_id}") - else: - # Cross-chain analysis - analysis = analytics.get_cross_chain_analysis() - - if not analysis: - error("No analytics data available") - raise click.Abort() - - # Overview data - overview_data = [ - {"Metric": "Total Chains", "Value": analysis["total_chains"]}, - {"Metric": "Active Chains", "Value": analysis["active_chains"]}, - {"Metric": "Total Alerts", "Value": analysis["alerts_summary"]["total_alerts"]}, - {"Metric": "Critical Alerts", "Value": analysis["alerts_summary"]["critical_alerts"]}, - {"Metric": "Total Memory Usage", "Value": f"{analysis['resource_usage']['total_memory_mb']:.1f}MB"}, - {"Metric": "Total Disk Usage", "Value": f"{analysis['resource_usage']['total_disk_mb']:.1f}MB"}, - {"Metric": "Total Clients", "Value": analysis["resource_usage"]["total_clients"]}, - {"Metric": "Total Agents", "Value": analysis["resource_usage"]["total_agents"]} - ] - - output(overview_data, ctx.obj.get('output_format', format), title="Cross-Chain Analysis Overview") - - # Performance comparison - if analysis["performance_comparison"]: - comparison_data = [ - { - "Chain ID": chain_id, - "TPS": f"{data['tps']:.2f}", - "Block Time": f"{data['block_time']:.2f}s", - "Health Score": f"{data['health_score']:.1f}/100" - } - for chain_id, data in analysis["performance_comparison"].items() - ] - - output(comparison_data, ctx.obj.get('output_format', format), title="Chain Performance Comparison") - - except Exception as e: - error(f"Error getting analytics summary: {str(e)}") - raise click.Abort() - -@analytics.command() -@click.option('--realtime', is_flag=True, help='Real-time monitoring') -@click.option('--interval', default=30, help='Update interval in seconds') -@click.option('--chain-id', help='Monitor specific chain') -@click.pass_context -def monitor(ctx, realtime, interval, chain_id): - """Monitor chain performance in real-time""" - try: - config = load_multichain_config() - analytics = ChainAnalytics(config) - - if realtime: - # Real-time monitoring - from rich.console import Console - from rich.live import Live - from rich.table import Table - import time - - console = Console() - - def generate_monitor_table(): - try: - # Collect latest metrics - asyncio.run(analytics.collect_all_metrics()) - - table = Table(title=f"Chain Monitor - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") - table.add_column("Chain ID", style="cyan") - table.add_column("TPS", style="green") - table.add_column("Block Time", style="yellow") - table.add_column("Health", style="red") - table.add_column("Alerts", style="magenta") - - if chain_id: - # Single chain monitoring - summary = analytics.get_chain_performance_summary(chain_id, 1) - if summary: - health_color = "green" if summary["health_score"] > 70 else "yellow" if summary["health_score"] > 40 else "red" - table.add_row( - chain_id, - f"{summary['statistics']['tps']['avg']:.2f}", - f"{summary['statistics']['block_time']['avg']:.2f}s", - f"[{health_color}]{summary['health_score']:.1f}[/{health_color}]", - str(summary["active_alerts"]) - ) - else: - # All chains monitoring - analysis = analytics.get_cross_chain_analysis() - for chain_id, data in analysis["performance_comparison"].items(): - health_color = "green" if data["health_score"] > 70 else "yellow" if data["health_score"] > 40 else "red" - table.add_row( - chain_id, - f"{data['tps']:.2f}", - f"{data['block_time']:.2f}s", - f"[{health_color}]{data['health_score']:.1f}[/{health_color}]", - str(len([a for a in analytics.alerts if a.chain_id == chain_id])) - ) - - return table - except Exception as e: - return f"Error collecting metrics: {e}" - - with Live(generate_monitor_table(), refresh_per_second=1) as live: - try: - while True: - live.update(generate_monitor_table()) - time.sleep(interval) - except KeyboardInterrupt: - console.print("\n[yellow]Monitoring stopped by user[/yellow]") - else: - # Single snapshot - asyncio.run(analytics.collect_all_metrics()) - - if chain_id: - summary = analytics.get_chain_performance_summary(chain_id, 1) - if not summary: - error(f"No data available for chain {chain_id}") - raise click.Abort() - - monitor_data = [ - {"Metric": "Chain ID", "Value": summary["chain_id"]}, - {"Metric": "Current TPS", "Value": f"{summary['statistics']['tps']['avg']:.2f}"}, - {"Metric": "Current Block Time", "Value": f"{summary['statistics']['block_time']['avg']:.2f}s"}, - {"Metric": "Health Score", "Value": f"{summary['health_score']:.1f}/100"}, - {"Metric": "Active Alerts", "Value": summary["active_alerts"]}, - {"Metric": "Memory Usage", "Value": f"{summary['latest_metrics']['memory_usage_mb']:.1f}MB"}, - {"Metric": "Disk Usage", "Value": f"{summary['latest_metrics']['disk_usage_mb']:.1f}MB"}, - {"Metric": "Active Nodes", "Value": summary["latest_metrics"]["active_nodes"]}, - {"Metric": "Client Count", "Value": summary["latest_metrics"]["client_count"]}, - {"Metric": "Agent Count", "Value": summary["latest_metrics"]["agent_count"]} - ] - - output(monitor_data, ctx.obj.get('output_format', 'table'), title=f"Chain Monitor: {chain_id}") - else: - analysis = analytics.get_cross_chain_analysis() - - monitor_data = [ - {"Metric": "Total Chains", "Value": analysis["total_chains"]}, - {"Metric": "Active Chains", "Value": analysis["active_chains"]}, - {"Metric": "Total Memory Usage", "Value": f"{analysis['resource_usage']['total_memory_mb']:.1f}MB"}, - {"Metric": "Total Disk Usage", "Value": f"{analysis['resource_usage']['total_disk_mb']:.1f}MB"}, - {"Metric": "Total Clients", "Value": analysis["resource_usage"]["total_clients"]}, - {"Metric": "Total Agents", "Value": analysis["resource_usage"]["total_agents"]}, - {"Metric": "Total Alerts", "Value": analysis["alerts_summary"]["total_alerts"]}, - {"Metric": "Critical Alerts", "Value": analysis["alerts_summary"]["critical_alerts"]} - ] - - output(monitor_data, ctx.obj.get('output_format', 'table'), title="System Monitor") - - except Exception as e: - error(f"Error during monitoring: {str(e)}") - raise click.Abort() - -@analytics.command() -@click.option('--chain-id', help='Specific chain ID for predictions') -@click.option('--hours', default=24, help='Prediction time horizon in hours') -@click.option('--format', type=click.Choice(['table', 'json']), default='table', help='Output format') -@click.pass_context -def predict(ctx, chain_id, hours, format): - """Predict chain performance""" - try: - config = load_multichain_config() - analytics = ChainAnalytics(config) - - # Collect current metrics first - asyncio.run(analytics.collect_all_metrics()) - - if chain_id: - # Single chain prediction - predictions = asyncio.run(analytics.predict_chain_performance(chain_id, hours)) - - if not predictions: - error(f"No prediction data available for chain {chain_id}") - raise click.Abort() - - prediction_data = [ - { - "Metric": pred.metric, - "Predicted Value": f"{pred.predicted_value:.2f}", - "Confidence": f"{pred.confidence:.1%}", - "Time Horizon": f"{pred.time_horizon_hours}h" - } - for pred in predictions - ] - - output(prediction_data, ctx.obj.get('output_format', format), title=f"Performance Predictions: {chain_id}") - else: - # All chains prediction - analysis = analytics.get_cross_chain_analysis() - all_predictions = {} - - for chain_id in analysis["performance_comparison"].keys(): - predictions = asyncio.run(analytics.predict_chain_performance(chain_id, hours)) - if predictions: - all_predictions[chain_id] = predictions - - if not all_predictions: - error("No prediction data available") - raise click.Abort() - - # Format predictions for display - prediction_data = [] - for chain_id, predictions in all_predictions.items(): - for pred in predictions: - prediction_data.append({ - "Chain ID": chain_id, - "Metric": pred.metric, - "Predicted Value": f"{pred.predicted_value:.2f}", - "Confidence": f"{pred.confidence:.1%}", - "Time Horizon": f"{pred.time_horizon_hours}h" - }) - - output(prediction_data, ctx.obj.get('output_format', format), title="Chain Performance Predictions") - - except Exception as e: - error(f"Error generating predictions: {str(e)}") - raise click.Abort() - -@analytics.command() -@click.option('--chain-id', help='Specific chain ID for recommendations') -@click.option('--format', type=click.Choice(['table', 'json']), default='table', help='Output format') -@click.pass_context -def optimize(ctx, chain_id, format): - """Get optimization recommendations""" - try: - config = load_multichain_config() - analytics = ChainAnalytics(config) - - # Collect current metrics first - asyncio.run(analytics.collect_all_metrics()) - - if chain_id: - # Single chain recommendations - recommendations = analytics.get_optimization_recommendations(chain_id) - - if not recommendations: - success(f"No optimization recommendations for chain {chain_id}") - return - - recommendation_data = [ - { - "Type": rec["type"], - "Priority": rec["priority"], - "Issue": rec["issue"], - "Current Value": rec["current_value"], - "Recommended Action": rec["recommended_action"], - "Expected Improvement": rec["expected_improvement"] - } - for rec in recommendations - ] - - output(recommendation_data, ctx.obj.get('output_format', format), title=f"Optimization Recommendations: {chain_id}") - else: - # All chains recommendations - analysis = analytics.get_cross_chain_analysis() - all_recommendations = {} - - for chain_id in analysis["performance_comparison"].keys(): - recommendations = analytics.get_optimization_recommendations(chain_id) - if recommendations: - all_recommendations[chain_id] = recommendations - - if not all_recommendations: - success("No optimization recommendations available") - return - - # Format recommendations for display - recommendation_data = [] - for chain_id, recommendations in all_recommendations.items(): - for rec in recommendations: - recommendation_data.append({ - "Chain ID": chain_id, - "Type": rec["type"], - "Priority": rec["priority"], - "Issue": rec["issue"], - "Current Value": rec["current_value"], - "Recommended Action": rec["recommended_action"] - }) - - output(recommendation_data, ctx.obj.get('output_format', format), title="Chain Optimization Recommendations") - - except Exception as e: - error(f"Error getting optimization recommendations: {str(e)}") - raise click.Abort() - -@analytics.command() -@click.option('--severity', type=click.Choice(['all', 'critical', 'warning']), default='all', help='Alert severity filter') -@click.option('--hours', default=24, help='Time range in hours') -@click.option('--format', type=click.Choice(['table', 'json']), default='table', help='Output format') -@click.pass_context -def alerts(ctx, severity, hours, format): - """View performance alerts""" - try: - config = load_multichain_config() - analytics = ChainAnalytics(config) - - # Collect current metrics first - asyncio.run(analytics.collect_all_metrics()) - - # Filter alerts - cutoff_time = datetime.now() - timedelta(hours=hours) - filtered_alerts = [ - alert for alert in analytics.alerts - if alert.timestamp >= cutoff_time - ] - - if severity != 'all': - filtered_alerts = [a for a in filtered_alerts if a.severity == severity] - - if not filtered_alerts: - success("No alerts found") - return - - alert_data = [ - { - "Chain ID": alert.chain_id, - "Type": alert.alert_type, - "Severity": alert.severity, - "Message": alert.message, - "Current Value": f"{alert.current_value:.2f}", - "Threshold": f"{alert.threshold:.2f}", - "Time": alert.timestamp.strftime("%Y-%m-%d %H:%M:%S") - } - for alert in filtered_alerts - ] - - output(alert_data, ctx.obj.get('output_format', format), title=f"Performance Alerts (Last {hours}h)") - - except Exception as e: - error(f"Error getting alerts: {str(e)}") - raise click.Abort() - -@analytics.command() -@click.option('--format', type=click.Choice(['json']), default='json', help='Output format') -@click.pass_context -def dashboard(ctx, format): - """Get complete dashboard data""" - try: - config = load_multichain_config() - analytics = ChainAnalytics(config) - - # Collect current metrics - asyncio.run(analytics.collect_all_metrics()) - - # Get dashboard data - dashboard_data = analytics.get_dashboard_data() - - if format == 'json': - import json - click.echo(json.dumps(dashboard_data, indent=2, default=str)) - else: - error("Dashboard data only available in JSON format") - raise click.Abort() - - except Exception as e: - error(f"Error getting dashboard data: {str(e)}") - raise click.Abort() diff --git a/cli/aitbc_cli/commands/chain.py.bak b/cli/aitbc_cli/commands/chain.py.bak deleted file mode 100755 index 1c3c7a60..00000000 --- a/cli/aitbc_cli/commands/chain.py.bak +++ /dev/null @@ -1,562 +0,0 @@ -"""Chain management commands for AITBC CLI""" - -import click -from typing import Optional -from ..core.chain_manager import ChainManager, ChainNotFoundError, NodeNotAvailableError -from ..core.config import MultiChainConfig, load_multichain_config -from ..models.chain import ChainType -from ..utils import output, error, success - -@click.group() -def chain(): - """Multi-chain management commands""" - pass - -@chain.command() -@click.option('--type', 'chain_type', type=click.Choice(['main', 'topic', 'private', 'all']), - default='all', help='Filter by chain type') -@click.option('--show-private', is_flag=True, help='Show private chains') -@click.option('--sort', type=click.Choice(['id', 'size', 'nodes', 'created']), - default='id', help='Sort by field') -@click.pass_context -def list(ctx, chain_type, show_private, sort): - """List all available chains""" - try: - config = load_multichain_config() - chain_manager = ChainManager(config) - - # Get chains - import asyncio - chains = asyncio.run(chain_manager.list_chains( - chain_type=ChainType(chain_type) if chain_type != 'all' else None, - include_private=show_private, - sort_by=sort - )) - - if not chains: - output("No chains found", ctx.obj.get('output_format', 'table')) - return - - # Format output - chains_data = [ - { - "Chain ID": chain.id, - "Type": chain.type.value, - "Purpose": chain.purpose, - "Name": chain.name, - "Size": f"{chain.size_mb:.1f}MB", - "Nodes": chain.node_count, - "Contracts": chain.contract_count, - "Clients": chain.client_count, - "Miners": chain.miner_count, - "Status": chain.status.value - } - for chain in chains - ] - - output(chains_data, ctx.obj.get('output_format', 'table'), title="Available Chains") - - except Exception as e: - error(f"Error listing chains: {str(e)}") - raise click.Abort() - -@chain.command() -@click.option('--chain-id', help='Specific chain ID to check status (shows all if not specified)') -@click.option('--detailed', is_flag=True, help='Show detailed status information') -@click.pass_context -def status(ctx, chain_id, detailed): - """Check status of chains""" - try: - config = load_multichain_config() - chain_manager = ChainManager(config) - - import asyncio - - if chain_id: - # Get specific chain status - chain_info = asyncio.run(chain_manager.get_chain_info(chain_id, detailed=detailed)) - - status_data = { - "Chain ID": chain_info.id, - "Name": chain_info.name, - "Type": chain_info.type.value, - "Status": chain_info.status.value, - "Block Height": chain_info.block_height, - "Active Nodes": chain_info.active_nodes, - "Total Nodes": chain_info.node_count - } - - if detailed: - status_data.update({ - "Consensus": chain_info.consensus_algorithm.value, - "TPS": f"{chain_info.tps:.1f}", - "Gas Price": f"{chain_info.gas_price / 1e9:.1f} gwei", - "Memory Usage": f"{chain_info.memory_usage_mb:.1f}MB" - }) - - output(status_data, ctx.obj.get('output_format', 'table'), title=f"Chain Status: {chain_id}") - else: - # Get all chains status - chains = asyncio.run(chain_manager.list_chains()) - - if not chains: - output({"message": "No chains found"}, ctx.obj.get('output_format', 'table')) - return - - status_list = [] - for chain in chains: - status_info = { - "Chain ID": chain.id, - "Name": chain.name, - "Type": chain.type.value, - "Status": chain.status.value, - "Block Height": chain.block_height, - "Active Nodes": chain.active_nodes - } - status_list.append(status_info) - - output(status_list, ctx.obj.get('output_format', 'table'), title="Chain Status Overview") - - except ChainNotFoundError: - error(f"Chain {chain_id} not found") - raise click.Abort() - except Exception as e: - error(f"Error getting chain status: {str(e)}") - raise click.Abort() - -@chain.command() -@click.argument('chain_id') -@click.option('--detailed', is_flag=True, help='Show detailed information') -@click.option('--metrics', is_flag=True, help='Show performance metrics') -@click.pass_context -def info(ctx, chain_id, detailed, metrics): - """Get detailed information about a chain""" - try: - config = load_multichain_config() - chain_manager = ChainManager(config) - - import asyncio - chain_info = asyncio.run(chain_manager.get_chain_info(chain_id, detailed, metrics)) - - # Basic information - basic_info = { - "Chain ID": chain_info.id, - "Type": chain_info.type.value, - "Purpose": chain_info.purpose, - "Name": chain_info.name, - "Description": chain_info.description or "No description", - "Status": chain_info.status.value, - "Created": chain_info.created_at.strftime("%Y-%m-%d %H:%M:%S"), - "Block Height": chain_info.block_height, - "Size": f"{chain_info.size_mb:.1f}MB" - } - - output(basic_info, ctx.obj.get('output_format', 'table'), title=f"Chain Information: {chain_id}") - - if detailed: - # Network details - network_info = { - "Total Nodes": chain_info.node_count, - "Active Nodes": chain_info.active_nodes, - "Consensus": chain_info.consensus_algorithm.value, - "Block Time": f"{chain_info.block_time}s", - "Clients": chain_info.client_count, - "Miners": chain_info.miner_count, - "Contracts": chain_info.contract_count, - "Agents": chain_info.agent_count, - "Privacy": chain_info.privacy.visibility, - "Access Control": chain_info.privacy.access_control - } - - output(network_info, ctx.obj.get('output_format', 'table'), title="Network Details") - - if metrics: - # Performance metrics - performance_info = { - "TPS": f"{chain_info.tps:.1f}", - "Avg Block Time": f"{chain_info.avg_block_time:.1f}s", - "Avg Gas Used": f"{chain_info.avg_gas_used:,}", - "Gas Price": f"{chain_info.gas_price / 1e9:.1f} gwei", - "Growth Rate": f"{chain_info.growth_rate_mb_per_day:.1f}MB/day", - "Memory Usage": f"{chain_info.memory_usage_mb:.1f}MB", - "Disk Usage": f"{chain_info.disk_usage_mb:.1f}MB" - } - - output(performance_info, ctx.obj.get('output_format', 'table'), title="Performance Metrics") - - except ChainNotFoundError: - error(f"Chain {chain_id} not found") - raise click.Abort() - except Exception as e: - error(f"Error getting chain info: {str(e)}") - raise click.Abort() - -@chain.command() -@click.argument('config_file', type=click.Path(exists=True)) -@click.option('--node', help='Target node for chain creation') -@click.option('--dry-run', is_flag=True, help='Show what would be created without actually creating') -@click.pass_context -def create(ctx, config_file, node, dry_run): - """Create a new chain from configuration file""" - try: - import yaml - from ..models.chain import ChainConfig - - config = load_multichain_config() - chain_manager = ChainManager(config) - - # Load and validate configuration - with open(config_file, 'r') as f: - config_data = yaml.safe_load(f) - - chain_config = ChainConfig(**config_data['chain']) - - if dry_run: - dry_run_info = { - "Chain Type": chain_config.type.value, - "Purpose": chain_config.purpose, - "Name": chain_config.name, - "Description": chain_config.description or "No description", - "Consensus": chain_config.consensus.algorithm.value, - "Privacy": chain_config.privacy.visibility, - "Target Node": node or "Auto-selected" - } - - output(dry_run_info, ctx.obj.get('output_format', 'table'), title="Dry Run - Chain Creation") - return - - # Create chain - chain_id = chain_manager.create_chain(chain_config, node) - - success(f"Chain created successfully!") - result = { - "Chain ID": chain_id, - "Type": chain_config.type.value, - "Purpose": chain_config.purpose, - "Name": chain_config.name, - "Node": node or "Auto-selected" - } - - output(result, ctx.obj.get('output_format', 'table')) - - if chain_config.privacy.visibility == "private": - success("Private chain created! Use access codes to invite participants.") - - except Exception as e: - error(f"Error creating chain: {str(e)}") - raise click.Abort() - -@chain.command() -@click.argument('chain_id') -@click.option('--force', is_flag=True, help='Force deletion without confirmation') -@click.option('--confirm', is_flag=True, help='Confirm deletion') -@click.pass_context -def delete(ctx, chain_id, force, confirm): - """Delete a chain permanently""" - try: - config = load_multichain_config() - chain_manager = ChainManager(config) - - # Get chain information for confirmation - import asyncio - chain_info = asyncio.run(chain_manager.get_chain_info(chain_id, detailed=True)) - - if not force: - # Show warning and confirmation - warning_info = { - "Chain ID": chain_id, - "Type": chain_info.type.value, - "Purpose": chain_info.purpose, - "Name": chain_info.name, - "Status": chain_info.status.value, - "Participants": chain_info.client_count, - "Transactions": "Multiple" # Would get actual count - } - - output(warning_info, ctx.obj.get('output_format', 'table'), title="Chain Deletion Warning") - - if not confirm: - error("To confirm deletion, use --confirm flag") - raise click.Abort() - - # Delete chain - import asyncio - is_success = asyncio.run(chain_manager.delete_chain(chain_id, force)) - - if is_success: - success(f"Chain {chain_id} deleted successfully!") - else: - error(f"Failed to delete chain {chain_id}") - raise click.Abort() - - except ChainNotFoundError: - error(f"Chain {chain_id} not found") - raise click.Abort() - except Exception as e: - error(f"Error deleting chain: {str(e)}") - raise click.Abort() - -@chain.command() -@click.argument('chain_id') -@click.argument('node_id') -@click.pass_context -def add(ctx, chain_id, node_id): - """Add a chain to a specific node""" - try: - config = load_multichain_config() - chain_manager = ChainManager(config) - - import asyncio - is_success = asyncio.run(chain_manager.add_chain_to_node(chain_id, node_id)) - - if is_success: - success(f"Chain {chain_id} added to node {node_id} successfully!") - else: - error(f"Failed to add chain {chain_id} to node {node_id}") - raise click.Abort() - - except Exception as e: - error(f"Error adding chain to node: {str(e)}") - raise click.Abort() - -@chain.command() -@click.argument('chain_id') -@click.argument('node_id') -@click.option('--migrate', is_flag=True, help='Migrate to another node before removal') -@click.pass_context -def remove(ctx, chain_id, node_id, migrate): - """Remove a chain from a specific node""" - try: - config = load_multichain_config() - chain_manager = ChainManager(config) - - is_success = chain_manager.remove_chain_from_node(chain_id, node_id, migrate) - - if is_success: - success(f"Chain {chain_id} removed from node {node_id} successfully!") - else: - error(f"Failed to remove chain {chain_id} from node {node_id}") - raise click.Abort() - - except Exception as e: - error(f"Error removing chain from node: {str(e)}") - raise click.Abort() - -@chain.command() -@click.argument('chain_id') -@click.argument('from_node') -@click.argument('to_node') -@click.option('--dry-run', is_flag=True, help='Show migration plan without executing') -@click.option('--verify', is_flag=True, help='Verify migration after completion') -@click.pass_context -def migrate(ctx, chain_id, from_node, to_node, dry_run, verify): - """Migrate a chain between nodes""" - try: - config = load_multichain_config() - chain_manager = ChainManager(config) - - migration_result = chain_manager.migrate_chain(chain_id, from_node, to_node, dry_run) - - if dry_run: - plan_info = { - "Chain ID": chain_id, - "Source Node": from_node, - "Target Node": to_node, - "Feasible": "Yes" if migration_result.success else "No", - "Estimated Time": f"{migration_result.transfer_time_seconds}s", - "Error": migration_result.error or "None" - } - - output(plan_info, ctx.obj.get('output_format', 'table'), title="Migration Plan") - return - - if migration_result.success: - success(f"Chain migration completed successfully!") - result = { - "Chain ID": chain_id, - "Source Node": from_node, - "Target Node": to_node, - "Blocks Transferred": migration_result.blocks_transferred, - "Transfer Time": f"{migration_result.transfer_time_seconds}s", - "Verification": "Passed" if migration_result.verification_passed else "Failed" - } - - output(result, ctx.obj.get('output_format', 'table')) - else: - error(f"Migration failed: {migration_result.error}") - raise click.Abort() - - except Exception as e: - error(f"Error during migration: {str(e)}") - raise click.Abort() - -@chain.command() -@click.argument('chain_id') -@click.option('--path', help='Backup directory path') -@click.option('--compress', is_flag=True, help='Compress backup') -@click.option('--verify', is_flag=True, help='Verify backup integrity') -@click.pass_context -def backup(ctx, chain_id, path, compress, verify): - """Backup chain data""" - try: - config = load_multichain_config() - chain_manager = ChainManager(config) - - import asyncio - backup_result = asyncio.run(chain_manager.backup_chain(chain_id, path, compress, verify)) - - success(f"Chain backup completed successfully!") - result = { - "Chain ID": chain_id, - "Backup File": backup_result.backup_file, - "Original Size": f"{backup_result.original_size_mb:.1f}MB", - "Backup Size": f"{backup_result.backup_size_mb:.1f}MB", - "Compression": f"{backup_result.compression_ratio:.1f}x" if compress else "None", - "Checksum": backup_result.checksum, - "Verification": "Passed" if backup_result.verification_passed else "Failed" - } - - output(result, ctx.obj.get('output_format', 'table')) - - except Exception as e: - error(f"Error during backup: {str(e)}") - raise click.Abort() - -@chain.command() -@click.argument('backup_file', type=click.Path(exists=True)) -@click.option('--node', help='Target node for restoration') -@click.option('--verify', is_flag=True, help='Verify restoration') -@click.pass_context -def restore(ctx, backup_file, node, verify): - """Restore chain from backup""" - try: - config = load_multichain_config() - chain_manager = ChainManager(config) - - import asyncio - restore_result = asyncio.run(chain_manager.restore_chain(backup_file, node, verify)) - - success(f"Chain restoration completed successfully!") - result = { - "Chain ID": restore_result.chain_id, - "Node": restore_result.node_id, - "Blocks Restored": restore_result.blocks_restored, - "Verification": "Passed" if restore_result.verification_passed else "Failed" - } - - output(result, ctx.obj.get('output_format', 'table')) - - except Exception as e: - error(f"Error during restoration: {str(e)}") - raise click.Abort() - -@chain.command() -@click.argument('chain_id') -@click.option('--realtime', is_flag=True, help='Real-time monitoring') -@click.option('--export', help='Export monitoring data to file') -@click.option('--interval', default=5, help='Update interval in seconds') -@click.pass_context -def monitor(ctx, chain_id, realtime, export, interval): - """Monitor chain activity""" - try: - config = load_multichain_config() - chain_manager = ChainManager(config) - - if realtime: - # Real-time monitoring (placeholder implementation) - from rich.console import Console - from rich.layout import Layout - from rich.live import Live - import time - - console = Console() - - def generate_monitor_layout(): - try: - import asyncio - chain_info = asyncio.run(chain_manager.get_chain_info(chain_id, detailed=True, metrics=True)) - - layout = Layout() - layout.split_column( - Layout(name="header", size=3), - Layout(name="stats"), - Layout(name="activity", size=10) - ) - - # Header - layout["header"].update( - f"Chain Monitor: {chain_id} - {chain_info.status.value.upper()}" - ) - - # Stats table - stats_data = [ - ["Block Height", str(chain_info.block_height)], - ["TPS", f"{chain_info.tps:.1f}"], - ["Active Nodes", str(chain_info.active_nodes)], - ["Gas Price", f"{chain_info.gas_price / 1e9:.1f} gwei"], - ["Memory Usage", f"{chain_info.memory_usage_mb:.1f}MB"], - ["Disk Usage", f"{chain_info.disk_usage_mb:.1f}MB"] - ] - - layout["stats"].update(str(stats_data)) - - # Recent activity (placeholder) - layout["activity"].update("Recent activity would be displayed here") - - return layout - except Exception as e: - return f"Error getting chain info: {e}" - - with Live(generate_monitor_layout(), refresh_per_second=1) as live: - try: - while True: - live.update(generate_monitor_layout()) - time.sleep(interval) - except KeyboardInterrupt: - console.print("\n[yellow]Monitoring stopped by user[/yellow]") - else: - # Single snapshot - import asyncio - chain_info = asyncio.run(chain_manager.get_chain_info(chain_id, detailed=True, metrics=True)) - - stats_data = [ - { - "Metric": "Block Height", - "Value": str(chain_info.block_height) - }, - { - "Metric": "TPS", - "Value": f"{chain_info.tps:.1f}" - }, - { - "Metric": "Active Nodes", - "Value": str(chain_info.active_nodes) - }, - { - "Metric": "Gas Price", - "Value": f"{chain_info.gas_price / 1e9:.1f} gwei" - }, - { - "Metric": "Memory Usage", - "Value": f"{chain_info.memory_usage_mb:.1f}MB" - }, - { - "Metric": "Disk Usage", - "Value": f"{chain_info.disk_usage_mb:.1f}MB" - } - ] - - output(stats_data, ctx.obj.get('output_format', 'table'), title=f"Chain Statistics: {chain_id}") - - if export: - import json - with open(export, 'w') as f: - json.dump(chain_info.dict(), f, indent=2, default=str) - success(f"Statistics exported to {export}") - - except ChainNotFoundError: - error(f"Chain {chain_id} not found") - raise click.Abort() - except Exception as e: - error(f"Error during monitoring: {str(e)}") - raise click.Abort() diff --git a/cli/aitbc_cli/commands/cross_chain.py.bak b/cli/aitbc_cli/commands/cross_chain.py.bak deleted file mode 100755 index 7eba4916..00000000 --- a/cli/aitbc_cli/commands/cross_chain.py.bak +++ /dev/null @@ -1,476 +0,0 @@ -"""Cross-chain trading commands for AITBC CLI""" - -import click -import httpx -import json -from typing import Optional -from tabulate import tabulate -from ..config import get_config -from ..utils import success, error, output - - -@click.group() -def cross_chain(): - """Cross-chain trading operations""" - pass - - -@cross_chain.command() -@click.option("--from-chain", help="Source chain ID") -@click.option("--to-chain", help="Target chain ID") -@click.option("--from-token", help="Source token symbol") -@click.option("--to-token", help="Target token symbol") -@click.pass_context -def rates(ctx, from_chain: Optional[str], to_chain: Optional[str], - from_token: Optional[str], to_token: Optional[str]): - """Get cross-chain exchange rates""" - config = ctx.obj['config'] - - try: - with httpx.Client() as client: - # Get rates from cross-chain exchange - response = client.get( - f"http://localhost:8001/api/v1/cross-chain/rates", - timeout=10 - ) - - if response.status_code == 200: - rates_data = response.json() - rates = rates_data.get('rates', {}) - - if from_chain and to_chain: - # Get specific rate - pair_key = f"{from_chain}-{to_chain}" - if pair_key in rates: - success(f"Exchange rate {from_chain} → {to_chain}: {rates[pair_key]}") - else: - error(f"No rate available for {from_chain} → {to_chain}") - else: - # Show all rates - success("Cross-chain exchange rates:") - rate_table = [] - for pair, rate in rates.items(): - chains = pair.split('-') - rate_table.append([chains[0], chains[1], f"{rate:.6f}"]) - - if rate_table: - headers = ["From Chain", "To Chain", "Rate"] - print(tabulate(rate_table, headers=headers, tablefmt="grid")) - else: - output("No cross-chain rates available") - else: - error(f"Failed to get cross-chain rates: {response.status_code}") - except Exception as e: - error(f"Network error: {e}") - - -@cross_chain.command() -@click.option("--from-chain", required=True, help="Source chain ID") -@click.option("--to-chain", required=True, help="Target chain ID") -@click.option("--from-token", required=True, help="Source token symbol") -@click.option("--to-token", required=True, help="Target token symbol") -@click.option("--amount", type=float, required=True, help="Amount to swap") -@click.option("--min-amount", type=float, help="Minimum amount to receive") -@click.option("--slippage", type=float, default=0.01, help="Slippage tolerance (0-0.1)") -@click.option("--address", help="User wallet address") -@click.pass_context -def swap(ctx, from_chain: str, to_chain: str, from_token: str, to_token: str, - amount: float, min_amount: Optional[float], slippage: float, address: Optional[str]): - """Create cross-chain swap""" - config = ctx.obj['config'] - - # Validate inputs - if from_chain == to_chain: - error("Source and target chains must be different") - return - - if amount <= 0: - error("Amount must be greater than 0") - return - - # Use default address if not provided - if not address: - address = config.get('default_address', '0x1234567890123456789012345678901234567890') - - # Calculate minimum amount if not provided - if not min_amount: - # Get rate first - try: - with httpx.Client() as client: - response = client.get( - f"http://localhost:8001/api/v1/cross-chain/rates", - timeout=10 - ) - if response.status_code == 200: - rates_data = response.json() - pair_key = f"{from_chain}-{to_chain}" - rate = rates_data.get('rates', {}).get(pair_key, 1.0) - min_amount = amount * rate * (1 - slippage) * 0.97 # Account for fees - else: - min_amount = amount * 0.95 # Conservative fallback - except: - min_amount = amount * 0.95 - - swap_data = { - "from_chain": from_chain, - "to_chain": to_chain, - "from_token": from_token, - "to_token": to_token, - "amount": amount, - "min_amount": min_amount, - "user_address": address, - "slippage_tolerance": slippage - } - - try: - with httpx.Client() as client: - response = client.post( - f"http://localhost:8001/api/v1/cross-chain/swap", - json=swap_data, - timeout=30 - ) - - if response.status_code == 200: - swap_result = response.json() - success("Cross-chain swap created successfully!") - output({ - "Swap ID": swap_result.get('swap_id'), - "From Chain": swap_result.get('from_chain'), - "To Chain": swap_result.get('to_chain'), - "Amount": swap_result.get('amount'), - "Expected Amount": swap_result.get('expected_amount'), - "Rate": swap_result.get('rate'), - "Total Fees": swap_result.get('total_fees'), - "Status": swap_result.get('status') - }, ctx.obj['output_format']) - - # Show swap ID for tracking - success(f"Track swap with: aitbc cross-chain status {swap_result.get('swap_id')}") - else: - error(f"Failed to create swap: {response.status_code}") - if response.text: - error(f"Details: {response.text}") - except Exception as e: - error(f"Network error: {e}") - - -@cross_chain.command() -@click.argument("swap_id") -@click.pass_context -def status(ctx, swap_id: str): - """Check cross-chain swap status""" - try: - with httpx.Client() as client: - response = client.get( - f"http://localhost:8001/api/v1/cross-chain/swap/{swap_id}", - timeout=10 - ) - - if response.status_code == 200: - swap_data = response.json() - success(f"Swap Status: {swap_data.get('status', 'unknown')}") - - # Display swap details - details = { - "Swap ID": swap_data.get('swap_id'), - "From Chain": swap_data.get('from_chain'), - "To Chain": swap_data.get('to_chain'), - "From Token": swap_data.get('from_token'), - "To Token": swap_data.get('to_token'), - "Amount": swap_data.get('amount'), - "Expected Amount": swap_data.get('expected_amount'), - "Actual Amount": swap_data.get('actual_amount'), - "Status": swap_data.get('status'), - "Created At": swap_data.get('created_at'), - "Completed At": swap_data.get('completed_at'), - "Bridge Fee": swap_data.get('bridge_fee'), - "From Tx Hash": swap_data.get('from_tx_hash'), - "To Tx Hash": swap_data.get('to_tx_hash') - } - - output(details, ctx.obj['output_format']) - - # Show additional status info - if swap_data.get('status') == 'completed': - success("✅ Swap completed successfully!") - elif swap_data.get('status') == 'failed': - error("❌ Swap failed") - if swap_data.get('error_message'): - error(f"Error: {swap_data['error_message']}") - elif swap_data.get('status') == 'pending': - success("⏳ Swap is pending...") - elif swap_data.get('status') == 'executing': - success("🔄 Swap is executing...") - elif swap_data.get('status') == 'refunded': - success("💰 Swap was refunded") - else: - error(f"Failed to get swap status: {response.status_code}") - except Exception as e: - error(f"Network error: {e}") - - -@cross_chain.command() -@click.option("--user-address", help="Filter by user address") -@click.option("--status", help="Filter by status") -@click.option("--limit", type=int, default=10, help="Number of swaps to show") -@click.pass_context -def swaps(ctx, user_address: Optional[str], status: Optional[str], limit: int): - """List cross-chain swaps""" - params = {} - if user_address: - params['user_address'] = user_address - if status: - params['status'] = status - - try: - with httpx.Client() as client: - response = client.get( - f"http://localhost:8001/api/v1/cross-chain/swaps", - params=params, - timeout=10 - ) - - if response.status_code == 200: - swaps_data = response.json() - swaps = swaps_data.get('swaps', []) - - if swaps: - success(f"Found {len(swaps)} cross-chain swaps:") - - # Create table - swap_table = [] - for swap in swaps[:limit]: - swap_table.append([ - swap.get('swap_id', '')[:8] + '...', - swap.get('from_chain', ''), - swap.get('to_chain', ''), - swap.get('amount', 0), - swap.get('status', ''), - swap.get('created_at', '')[:19] - ]) - - table(["ID", "From", "To", "Amount", "Status", "Created"], swap_table) - - if len(swaps) > limit: - success(f"Showing {limit} of {len(swaps)} total swaps") - else: - success("No cross-chain swaps found") - else: - error(f"Failed to get swaps: {response.status_code}") - except Exception as e: - error(f"Network error: {e}") - - -@cross_chain.command() -@click.option("--source-chain", required=True, help="Source chain ID") -@click.option("--target-chain", required=True, help="Target chain ID") -@click.option("--token", required=True, help="Token to bridge") -@click.option("--amount", type=float, required=True, help="Amount to bridge") -@click.option("--recipient", help="Recipient address") -@click.pass_context -def bridge(ctx, source_chain: str, target_chain: str, token: str, - amount: float, recipient: Optional[str]): - """Create cross-chain bridge transaction""" - config = ctx.obj['config'] - - # Validate inputs - if source_chain == target_chain: - error("Source and target chains must be different") - return - - if amount <= 0: - error("Amount must be greater than 0") - return - - # Use default recipient if not provided - if not recipient: - recipient = config.get('default_address', '0x1234567890123456789012345678901234567890') - - bridge_data = { - "source_chain": source_chain, - "target_chain": target_chain, - "token": token, - "amount": amount, - "recipient_address": recipient - } - - try: - with httpx.Client() as client: - response = client.post( - f"http://localhost:8001/api/v1/cross-chain/bridge", - json=bridge_data, - timeout=30 - ) - - if response.status_code == 200: - bridge_result = response.json() - success("Cross-chain bridge created successfully!") - output({ - "Bridge ID": bridge_result.get('bridge_id'), - "Source Chain": bridge_result.get('source_chain'), - "Target Chain": bridge_result.get('target_chain'), - "Token": bridge_result.get('token'), - "Amount": bridge_result.get('amount'), - "Bridge Fee": bridge_result.get('bridge_fee'), - "Status": bridge_result.get('status') - }, ctx.obj['output_format']) - - # Show bridge ID for tracking - success(f"Track bridge with: aitbc cross-chain bridge-status {bridge_result.get('bridge_id')}") - else: - error(f"Failed to create bridge: {response.status_code}") - if response.text: - error(f"Details: {response.text}") - except Exception as e: - error(f"Network error: {e}") - - -@cross_chain.command() -@click.argument("bridge_id") -@click.pass_context -def bridge_status(ctx, bridge_id: str): - """Check cross-chain bridge status""" - try: - with httpx.Client() as client: - response = client.get( - f"http://localhost:8001/api/v1/cross-chain/bridge/{bridge_id}", - timeout=10 - ) - - if response.status_code == 200: - bridge_data = response.json() - success(f"Bridge Status: {bridge_data.get('status', 'unknown')}") - - # Display bridge details - details = { - "Bridge ID": bridge_data.get('bridge_id'), - "Source Chain": bridge_data.get('source_chain'), - "Target Chain": bridge_data.get('target_chain'), - "Token": bridge_data.get('token'), - "Amount": bridge_data.get('amount'), - "Recipient Address": bridge_data.get('recipient_address'), - "Status": bridge_data.get('status'), - "Created At": bridge_data.get('created_at'), - "Completed At": bridge_data.get('completed_at'), - "Bridge Fee": bridge_data.get('bridge_fee'), - "Source Tx Hash": bridge_data.get('source_tx_hash'), - "Target Tx Hash": bridge_data.get('target_tx_hash') - } - - output(details, ctx.obj['output_format']) - - # Show additional status info - if bridge_data.get('status') == 'completed': - success("✅ Bridge completed successfully!") - elif bridge_data.get('status') == 'failed': - error("❌ Bridge failed") - if bridge_data.get('error_message'): - error(f"Error: {bridge_data['error_message']}") - elif bridge_data.get('status') == 'pending': - success("⏳ Bridge is pending...") - elif bridge_data.get('status') == 'locked': - success("🔒 Bridge is locked...") - elif bridge_data.get('status') == 'transferred': - success("🔄 Bridge is transferring...") - else: - error(f"Failed to get bridge status: {response.status_code}") - except Exception as e: - error(f"Network error: {e}") - - -@cross_chain.command() -@click.pass_context -def pools(ctx): - """Show cross-chain liquidity pools""" - try: - with httpx.Client() as client: - response = client.get( - f"http://localhost:8001/api/v1/cross-chain/pools", - timeout=10 - ) - - if response.status_code == 200: - pools_data = response.json() - pools = pools_data.get('pools', []) - - if pools: - success(f"Found {len(pools)} cross-chain liquidity pools:") - - # Create table - pool_table = [] - for pool in pools: - pool_table.append([ - pool.get('pool_id', ''), - pool.get('token_a', ''), - pool.get('token_b', ''), - pool.get('chain_a', ''), - pool.get('chain_b', ''), - f"{pool.get('reserve_a', 0):.2f}", - f"{pool.get('reserve_b', 0):.2f}", - f"{pool.get('total_liquidity', 0):.2f}", - f"{pool.get('apr', 0):.2%}" - ]) - - table(["Pool ID", "Token A", "Token B", "Chain A", "Chain B", - "Reserve A", "Reserve B", "Liquidity", "APR"], pool_table) - else: - success("No cross-chain liquidity pools found") - else: - error(f"Failed to get pools: {response.status_code}") - except Exception as e: - error(f"Network error: {e}") - - -@cross_chain.command() -@click.pass_context -def stats(ctx): - """Show cross-chain trading statistics""" - try: - with httpx.Client() as client: - response = client.get( - f"http://localhost:8001/api/v1/cross-chain/stats", - timeout=10 - ) - - if response.status_code == 200: - stats_data = response.json() - - success("Cross-Chain Trading Statistics:") - - # Show swap stats - swap_stats = stats_data.get('swap_stats', []) - if swap_stats: - success("Swap Statistics:") - swap_table = [] - for stat in swap_stats: - swap_table.append([ - stat.get('status', ''), - stat.get('count', 0), - f"{stat.get('volume', 0):.2f}" - ]) - table(["Status", "Count", "Volume"], swap_table) - - # Show bridge stats - bridge_stats = stats_data.get('bridge_stats', []) - if bridge_stats: - success("Bridge Statistics:") - bridge_table = [] - for stat in bridge_stats: - bridge_table.append([ - stat.get('status', ''), - stat.get('count', 0), - f"{stat.get('volume', 0):.2f}" - ]) - table(["Status", "Count", "Volume"], bridge_table) - - # Show overall stats - success("Overall Statistics:") - output({ - "Total Volume": f"{stats_data.get('total_volume', 0):.2f}", - "Supported Chains": ", ".join(stats_data.get('supported_chains', [])), - "Last Updated": stats_data.get('timestamp', '') - }, ctx.obj['output_format']) - else: - error(f"Failed to get stats: {response.status_code}") - except Exception as e: - error(f"Network error: {e}") diff --git a/cli/aitbc_cli/commands/deployment.py.bak b/cli/aitbc_cli/commands/deployment.py.bak deleted file mode 100755 index 54afde49..00000000 --- a/cli/aitbc_cli/commands/deployment.py.bak +++ /dev/null @@ -1,378 +0,0 @@ -"""Production deployment and scaling commands for AITBC CLI""" - -import click -import asyncio -import json -from datetime import datetime -from typing import Optional -from ..core.deployment import ( - ProductionDeployment, ScalingPolicy, DeploymentStatus -) -from ..utils import output, error, success - -@click.group() -def deploy(): - """Production deployment and scaling commands""" - pass - -@deploy.command() -@click.argument('name') -@click.argument('environment') -@click.argument('region') -@click.argument('instance_type') -@click.argument('min_instances', type=int) -@click.argument('max_instances', type=int) -@click.argument('desired_instances', type=int) -@click.argument('port', type=int) -@click.argument('domain') -@click.option('--db-host', default='localhost', help='Database host') -@click.option('--db-port', default=5432, help='Database port') -@click.option('--db-name', default='aitbc', help='Database name') -@click.pass_context -def create(ctx, name, environment, region, instance_type, min_instances, max_instances, desired_instances, port, domain, db_host, db_port, db_name): - """Create a new deployment configuration""" - try: - deployment = ProductionDeployment() - - # Database configuration - database_config = { - "host": db_host, - "port": db_port, - "name": db_name, - "ssl_enabled": True if environment == "production" else False - } - - # Create deployment - deployment_id = asyncio.run(deployment.create_deployment( - name=name, - environment=environment, - region=region, - instance_type=instance_type, - min_instances=min_instances, - max_instances=max_instances, - desired_instances=desired_instances, - port=port, - domain=domain, - database_config=database_config - )) - - if deployment_id: - success(f"Deployment configuration created! ID: {deployment_id}") - - deployment_data = { - "Deployment ID": deployment_id, - "Name": name, - "Environment": environment, - "Region": region, - "Instance Type": instance_type, - "Min Instances": min_instances, - "Max Instances": max_instances, - "Desired Instances": desired_instances, - "Port": port, - "Domain": domain, - "Status": "pending", - "Created": datetime.now().strftime("%Y-%m-%d %H:%M:%S") - } - - output(deployment_data, ctx.obj.get('output_format', 'table')) - else: - error("Failed to create deployment configuration") - raise click.Abort() - - except Exception as e: - error(f"Error creating deployment: {str(e)}") - raise click.Abort() - -@deploy.command() -@click.argument('deployment_id') -@click.pass_context -def start(ctx, deployment_id): - """Deploy the application to production""" - try: - deployment = ProductionDeployment() - - # Deploy application - success_deploy = asyncio.run(deployment.deploy_application(deployment_id)) - - if success_deploy: - success(f"Deployment {deployment_id} started successfully!") - - deployment_data = { - "Deployment ID": deployment_id, - "Status": "running", - "Started": datetime.now().strftime("%Y-%m-%d %H:%M:%S") - } - - output(deployment_data, ctx.obj.get('output_format', 'table')) - else: - error(f"Failed to start deployment {deployment_id}") - raise click.Abort() - - except Exception as e: - error(f"Error starting deployment: {str(e)}") - raise click.Abort() - -@deploy.command() -@click.argument('deployment_id') -@click.argument('target_instances', type=int) -@click.option('--reason', default='manual', help='Scaling reason') -@click.pass_context -def scale(ctx, deployment_id, target_instances, reason): - """Scale a deployment to target instance count""" - try: - deployment = ProductionDeployment() - - # Scale deployment - success_scale = asyncio.run(deployment.scale_deployment(deployment_id, target_instances, reason)) - - if success_scale: - success(f"Deployment {deployment_id} scaled to {target_instances} instances!") - - scaling_data = { - "Deployment ID": deployment_id, - "Target Instances": target_instances, - "Reason": reason, - "Status": "completed", - "Scaled": datetime.now().strftime("%Y-%m-%d %H:%M:%S") - } - - output(scaling_data, ctx.obj.get('output_format', 'table')) - else: - error(f"Failed to scale deployment {deployment_id}") - raise click.Abort() - - except Exception as e: - error(f"Error scaling deployment: {str(e)}") - raise click.Abort() - -@deploy.command() -@click.argument('deployment_id') -@click.pass_context -def status(ctx, deployment_id): - """Get comprehensive deployment status""" - try: - deployment = ProductionDeployment() - - # Get deployment status - status_data = asyncio.run(deployment.get_deployment_status(deployment_id)) - - if not status_data: - error(f"Deployment {deployment_id} not found") - raise click.Abort() - - # Format deployment info - deployment_info = status_data["deployment"] - info_data = [ - {"Metric": "Deployment ID", "Value": deployment_info["deployment_id"]}, - {"Metric": "Name", "Value": deployment_info["name"]}, - {"Metric": "Environment", "Value": deployment_info["environment"]}, - {"Metric": "Region", "Value": deployment_info["region"]}, - {"Metric": "Instance Type", "Value": deployment_info["instance_type"]}, - {"Metric": "Min Instances", "Value": deployment_info["min_instances"]}, - {"Metric": "Max Instances", "Value": deployment_info["max_instances"]}, - {"Metric": "Desired Instances", "Value": deployment_info["desired_instances"]}, - {"Metric": "Port", "Value": deployment_info["port"]}, - {"Metric": "Domain", "Value": deployment_info["domain"]}, - {"Metric": "Health Status", "Value": "Healthy" if status_data["health_status"] else "Unhealthy"}, - {"Metric": "Uptime", "Value": f"{status_data['uptime_percentage']:.2f}%"} - ] - - output(info_data, ctx.obj.get('output_format', 'table'), title=f"Deployment Status: {deployment_id}") - - # Show metrics if available - if status_data["metrics"]: - metrics = status_data["metrics"] - metrics_data = [ - {"Metric": "CPU Usage", "Value": f"{metrics['cpu_usage']:.1f}%"}, - {"Metric": "Memory Usage", "Value": f"{metrics['memory_usage']:.1f}%"}, - {"Metric": "Disk Usage", "Value": f"{metrics['disk_usage']:.1f}%"}, - {"Metric": "Request Count", "Value": metrics['request_count']}, - {"Metric": "Error Rate", "Value": f"{metrics['error_rate']:.2f}%"}, - {"Metric": "Response Time", "Value": f"{metrics['response_time']:.1f}ms"}, - {"Metric": "Active Instances", "Value": metrics['active_instances']} - ] - - output(metrics_data, ctx.obj.get('output_format', 'table'), title="Performance Metrics") - - # Show recent scaling events - if status_data["recent_scaling_events"]: - events = status_data["recent_scaling_events"] - events_data = [ - { - "Event ID": event["event_id"][:8], - "Type": event["scaling_type"], - "From": event["old_instances"], - "To": event["new_instances"], - "Reason": event["trigger_reason"], - "Success": "Yes" if event["success"] else "No", - "Time": event["triggered_at"] - } - for event in events - ] - - output(events_data, ctx.obj.get('output_format', 'table'), title="Recent Scaling Events") - - except Exception as e: - error(f"Error getting deployment status: {str(e)}") - raise click.Abort() - -@deploy.command() -@click.option('--format', type=click.Choice(['table', 'json']), default='table', help='Output format') -@click.pass_context -def overview(ctx, format): - """Get overview of all deployments""" - try: - deployment = ProductionDeployment() - - # Get cluster overview - overview_data = asyncio.run(deployment.get_cluster_overview()) - - if not overview_data: - error("No deployment data available") - raise click.Abort() - - # Cluster metrics - cluster_data = [ - {"Metric": "Total Deployments", "Value": overview_data["total_deployments"]}, - {"Metric": "Running Deployments", "Value": overview_data["running_deployments"]}, - {"Metric": "Total Instances", "Value": overview_data["total_instances"]}, - {"Metric": "Health Check Coverage", "Value": f"{overview_data['health_check_coverage']:.1%}"}, - {"Metric": "Recent Scaling Events", "Value": overview_data["recent_scaling_events"]}, - {"Metric": "Scaling Success Rate", "Value": f"{overview_data['successful_scaling_rate']:.1%}"} - ] - - output(cluster_data, ctx.obj.get('output_format', format), title="Cluster Overview") - - # Aggregate metrics - if "aggregate_metrics" in overview_data: - metrics = overview_data["aggregate_metrics"] - metrics_data = [ - {"Metric": "Average CPU Usage", "Value": f"{metrics['total_cpu_usage']:.1f}%"}, - {"Metric": "Average Memory Usage", "Value": f"{metrics['total_memory_usage']:.1f}%"}, - {"Metric": "Average Disk Usage", "Value": f"{metrics['total_disk_usage']:.1f}%"}, - {"Metric": "Average Response Time", "Value": f"{metrics['average_response_time']:.1f}ms"}, - {"Metric": "Average Error Rate", "Value": f"{metrics['average_error_rate']:.2f}%"}, - {"Metric": "Average Uptime", "Value": f"{metrics['average_uptime']:.1f}%"} - ] - - output(metrics_data, ctx.obj.get('output_format', format), title="Aggregate Performance Metrics") - - except Exception as e: - error(f"Error getting cluster overview: {str(e)}") - raise click.Abort() - -@deploy.command() -@click.argument('deployment_id') -@click.option('--interval', default=60, help='Update interval in seconds') -@click.pass_context -def monitor(ctx, deployment_id, interval): - """Monitor deployment performance in real-time""" - try: - deployment = ProductionDeployment() - - # Real-time monitoring - from rich.console import Console - from rich.live import Live - from rich.table import Table - import time - - console = Console() - - def generate_monitor_table(): - try: - status_data = asyncio.run(deployment.get_deployment_status(deployment_id)) - - if not status_data: - return f"Deployment {deployment_id} not found" - - deployment_info = status_data["deployment"] - metrics = status_data.get("metrics") - - table = Table(title=f"Deployment Monitor - {deployment_info['name']} ({deployment_id[:8]}) - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") - table.add_column("Metric", style="cyan") - table.add_column("Value", style="green") - - table.add_row("Environment", deployment_info["environment"]) - table.add_row("Desired Instances", str(deployment_info["desired_instances"])) - table.add_row("Health Status", "✅ Healthy" if status_data["health_status"] else "❌ Unhealthy") - table.add_row("Uptime", f"{status_data['uptime_percentage']:.2f}%") - - if metrics: - table.add_row("CPU Usage", f"{metrics['cpu_usage']:.1f}%") - table.add_row("Memory Usage", f"{metrics['memory_usage']:.1f}%") - table.add_row("Disk Usage", f"{metrics['disk_usage']:.1f}%") - table.add_row("Request Count", str(metrics['request_count'])) - table.add_row("Error Rate", f"{metrics['error_rate']:.2f}%") - table.add_row("Response Time", f"{metrics['response_time']:.1f}ms") - table.add_row("Active Instances", str(metrics['active_instances'])) - - return table - except Exception as e: - return f"Error getting deployment data: {e}" - - with Live(generate_monitor_table(), refresh_per_second=1) as live: - try: - while True: - live.update(generate_monitor_table()) - time.sleep(interval) - except KeyboardInterrupt: - console.print("\n[yellow]Monitoring stopped by user[/yellow]") - - except Exception as e: - error(f"Error during monitoring: {str(e)}") - raise click.Abort() - -@deploy.command() -@click.argument('deployment_id') -@click.pass_context -def auto_scale(ctx, deployment_id): - """Trigger auto-scaling evaluation for a deployment""" - try: - deployment = ProductionDeployment() - - # Trigger auto-scaling - success_auto = asyncio.run(deployment.auto_scale_deployment(deployment_id)) - - if success_auto: - success(f"Auto-scaling evaluation completed for deployment {deployment_id}") - else: - error(f"Auto-scaling evaluation failed for deployment {deployment_id}") - raise click.Abort() - - except Exception as e: - error(f"Error in auto-scaling: {str(e)}") - raise click.Abort() - -@deploy.command() -@click.option('--format', type=click.Choice(['table', 'json']), default='table', help='Output format') -@click.pass_context -def list_deployments(ctx, format): - """List all deployments""" - try: - deployment = ProductionDeployment() - - # Get all deployment statuses - deployments = [] - for deployment_id in deployment.deployments.keys(): - status_data = asyncio.run(deployment.get_deployment_status(deployment_id)) - if status_data: - deployment_info = status_data["deployment"] - deployments.append({ - "Deployment ID": deployment_info["deployment_id"][:8], - "Name": deployment_info["name"], - "Environment": deployment_info["environment"], - "Instances": f"{deployment_info['desired_instances']}/{deployment_info['max_instances']}", - "Status": "Running" if status_data["health_status"] else "Stopped", - "Uptime": f"{status_data['uptime_percentage']:.1f}%", - "Created": deployment_info["created_at"] - }) - - if not deployments: - output("No deployments found", ctx.obj.get('output_format', 'table')) - return - - output(deployments, ctx.obj.get('output_format', format), title="All Deployments") - - except Exception as e: - error(f"Error listing deployments: {str(e)}") - raise click.Abort() diff --git a/cli/aitbc_cli/commands/exchange.py.bak b/cli/aitbc_cli/commands/exchange.py.bak deleted file mode 100755 index 3d822185..00000000 --- a/cli/aitbc_cli/commands/exchange.py.bak +++ /dev/null @@ -1,981 +0,0 @@ -"""Exchange integration commands for AITBC CLI""" - -import click -import httpx -import json -import os -from pathlib import Path -from typing import Optional, Dict, Any, List -from datetime import datetime -from ..utils import output, error, success, warning -from ..config import get_config - - -@click.group() -def exchange(): - """Exchange integration and trading management commands""" - pass - - -@exchange.command() -@click.option("--name", required=True, help="Exchange name (e.g., Binance, Coinbase, Kraken)") -@click.option("--api-key", required=True, help="Exchange API key") -@click.option("--secret-key", help="Exchange API secret key") -@click.option("--sandbox", is_flag=True, help="Use sandbox/testnet environment") -@click.option("--description", help="Exchange description") -@click.pass_context -def register(ctx, name: str, api_key: str, secret_key: Optional[str], sandbox: bool, description: Optional[str]): - """Register a new exchange integration""" - config = get_config() - - # Create exchange configuration - exchange_config = { - "name": name, - "api_key": api_key, - "secret_key": secret_key or "NOT_SET", - "sandbox": sandbox, - "description": description or f"{name} exchange integration", - "created_at": datetime.utcnow().isoformat(), - "status": "active", - "trading_pairs": [], - "last_sync": None - } - - # Store exchange configuration - exchanges_file = Path.home() / ".aitbc" / "exchanges.json" - exchanges_file.parent.mkdir(parents=True, exist_ok=True) - - # Load existing exchanges - exchanges = {} - if exchanges_file.exists(): - with open(exchanges_file, 'r') as f: - exchanges = json.load(f) - - # Add new exchange - exchanges[name.lower()] = exchange_config - - # Save exchanges - with open(exchanges_file, 'w') as f: - json.dump(exchanges, f, indent=2) - - success(f"Exchange '{name}' registered successfully") - output({ - "exchange": name, - "status": "registered", - "sandbox": sandbox, - "created_at": exchange_config["created_at"] - }) - - -@exchange.command() -@click.option("--base-asset", required=True, help="Base asset symbol (e.g., AITBC)") -@click.option("--quote-asset", required=True, help="Quote asset symbol (e.g., BTC)") -@click.option("--exchange", required=True, help="Exchange name") -@click.option("--min-order-size", type=float, default=0.001, help="Minimum order size") -@click.option("--price-precision", type=int, default=8, help="Price precision") -@click.option("--quantity-precision", type=int, default=8, help="Quantity precision") -@click.pass_context -def create_pair(ctx, base_asset: str, quote_asset: str, exchange: str, min_order_size: float, price_precision: int, quantity_precision: int): - """Create a new trading pair""" - pair_symbol = f"{base_asset}/{quote_asset}" - - # Load exchanges - exchanges_file = Path.home() / ".aitbc" / "exchanges.json" - if not exchanges_file.exists(): - error("No exchanges registered. Use 'aitbc exchange register' first.") - return - - with open(exchanges_file, 'r') as f: - exchanges = json.load(f) - - if exchange.lower() not in exchanges: - error(f"Exchange '{exchange}' not registered.") - return - - # Create trading pair configuration - pair_config = { - "symbol": pair_symbol, - "base_asset": base_asset, - "quote_asset": quote_asset, - "exchange": exchange, - "min_order_size": min_order_size, - "price_precision": price_precision, - "quantity_precision": quantity_precision, - "status": "active", - "created_at": datetime.utcnow().isoformat(), - "trading_enabled": False - } - - # Update exchange with new pair - exchanges[exchange.lower()]["trading_pairs"].append(pair_config) - - # Save exchanges - with open(exchanges_file, 'w') as f: - json.dump(exchanges, f, indent=2) - - success(f"Trading pair '{pair_symbol}' created on {exchange}") - output({ - "pair": pair_symbol, - "exchange": exchange, - "status": "created", - "min_order_size": min_order_size, - "created_at": pair_config["created_at"] - }) - - -@exchange.command() -@click.option("--pair", required=True, help="Trading pair symbol (e.g., AITBC/BTC)") -@click.option("--price", type=float, help="Initial price for the pair") -@click.option("--base-liquidity", type=float, default=10000, help="Base asset liquidity amount") -@click.option("--quote-liquidity", type=float, default=10000, help="Quote asset liquidity amount") -@click.option("--exchange", help="Exchange name (if not specified, uses first available)") -@click.pass_context -def start_trading(ctx, pair: str, price: Optional[float], base_liquidity: float, quote_liquidity: float, exchange: Optional[str]): - """Start trading for a specific pair""" - - # Load exchanges - exchanges_file = Path.home() / ".aitbc" / "exchanges.json" - if not exchanges_file.exists(): - error("No exchanges registered. Use 'aitbc exchange register' first.") - return - - with open(exchanges_file, 'r') as f: - exchanges = json.load(f) - - # Find the pair - target_exchange = None - target_pair = None - - for exchange_name, exchange_data in exchanges.items(): - for pair_config in exchange_data.get("trading_pairs", []): - if pair_config["symbol"] == pair: - target_exchange = exchange_name - target_pair = pair_config - break - if target_pair: - break - - if not target_pair: - error(f"Trading pair '{pair}' not found. Create it first with 'aitbc exchange create-pair'.") - return - - # Update pair to enable trading - target_pair["trading_enabled"] = True - target_pair["started_at"] = datetime.utcnow().isoformat() - target_pair["initial_price"] = price or 0.00001 # Default price for AITBC - target_pair["base_liquidity"] = base_liquidity - target_pair["quote_liquidity"] = quote_liquidity - - # Save exchanges - with open(exchanges_file, 'w') as f: - json.dump(exchanges, f, indent=2) - - success(f"Trading started for pair '{pair}' on {target_exchange}") - output({ - "pair": pair, - "exchange": target_exchange, - "status": "trading_active", - "initial_price": target_pair["initial_price"], - "base_liquidity": base_liquidity, - "quote_liquidity": quote_liquidity, - "started_at": target_pair["started_at"] - }) - - -@exchange.command() -@click.option("--pair", help="Trading pair symbol (e.g., AITBC/BTC)") -@click.option("--exchange", help="Exchange name") -@click.option("--real-time", is_flag=True, help="Enable real-time monitoring") -@click.option("--interval", type=int, default=60, help="Update interval in seconds") -@click.pass_context -def monitor(ctx, pair: Optional[str], exchange: Optional[str], real_time: bool, interval: int): - """Monitor exchange trading activity""" - - # Load exchanges - exchanges_file = Path.home() / ".aitbc" / "exchanges.json" - if not exchanges_file.exists(): - error("No exchanges registered. Use 'aitbc exchange register' first.") - return - - with open(exchanges_file, 'r') as f: - exchanges = json.load(f) - - # Filter exchanges and pairs - monitoring_data = [] - - for exchange_name, exchange_data in exchanges.items(): - if exchange and exchange_name != exchange.lower(): - continue - - for pair_config in exchange_data.get("trading_pairs", []): - if pair and pair_config["symbol"] != pair: - continue - - monitoring_data.append({ - "exchange": exchange_name, - "pair": pair_config["symbol"], - "status": "active" if pair_config.get("trading_enabled") else "inactive", - "created_at": pair_config.get("created_at"), - "started_at": pair_config.get("started_at"), - "initial_price": pair_config.get("initial_price"), - "base_liquidity": pair_config.get("base_liquidity"), - "quote_liquidity": pair_config.get("quote_liquidity") - }) - - if not monitoring_data: - error("No trading pairs found for monitoring.") - return - - # Display monitoring data - output({ - "monitoring_active": True, - "real_time": real_time, - "interval": interval, - "pairs": monitoring_data, - "total_pairs": len(monitoring_data) - }) - - if real_time: - warning(f"Real-time monitoring enabled. Updates every {interval} seconds.") - # Note: In a real implementation, this would start a background monitoring process - - -@exchange.command() -@click.option("--pair", required=True, help="Trading pair symbol (e.g., AITBC/BTC)") -@click.option("--amount", type=float, required=True, help="Liquidity amount") -@click.option("--side", type=click.Choice(['buy', 'sell']), default='both', help="Side to provide liquidity") -@click.option("--exchange", help="Exchange name") -@click.pass_context -def add_liquidity(ctx, pair: str, amount: float, side: str, exchange: Optional[str]): - """Add liquidity to a trading pair""" - - # Load exchanges - exchanges_file = Path.home() / ".aitbc" / "exchanges.json" - if not exchanges_file.exists(): - error("No exchanges registered. Use 'aitbc exchange register' first.") - return - - with open(exchanges_file, 'r') as f: - exchanges = json.load(f) - - # Find the pair - target_exchange = None - target_pair = None - - for exchange_name, exchange_data in exchanges.items(): - if exchange and exchange_name != exchange.lower(): - continue - - for pair_config in exchange_data.get("trading_pairs", []): - if pair_config["symbol"] == pair: - target_exchange = exchange_name - target_pair = pair_config - break - if target_pair: - break - - if not target_pair: - error(f"Trading pair '{pair}' not found.") - return - - # Add liquidity - if side == 'buy' or side == 'both': - target_pair["quote_liquidity"] = target_pair.get("quote_liquidity", 0) + amount - if side == 'sell' or side == 'both': - target_pair["base_liquidity"] = target_pair.get("base_liquidity", 0) + amount - - target_pair["liquidity_updated_at"] = datetime.utcnow().isoformat() - - # Save exchanges - with open(exchanges_file, 'w') as f: - json.dump(exchanges, f, indent=2) - - success(f"Added {amount} liquidity to {pair} on {target_exchange} ({side} side)") - output({ - "pair": pair, - "exchange": target_exchange, - "amount": amount, - "side": side, - "base_liquidity": target_pair.get("base_liquidity"), - "quote_liquidity": target_pair.get("quote_liquidity"), - "updated_at": target_pair["liquidity_updated_at"] - }) - - -@exchange.command() -@click.pass_context -def list(ctx): - """List all registered exchanges and trading pairs""" - - # Load exchanges - exchanges_file = Path.home() / ".aitbc" / "exchanges.json" - if not exchanges_file.exists(): - warning("No exchanges registered.") - return - - with open(exchanges_file, 'r') as f: - exchanges = json.load(f) - - # Format output - exchange_list = [] - for exchange_name, exchange_data in exchanges.items(): - exchange_info = { - "name": exchange_data["name"], - "status": exchange_data["status"], - "sandbox": exchange_data.get("sandbox", False), - "trading_pairs": len(exchange_data.get("trading_pairs", [])), - "created_at": exchange_data["created_at"] - } - exchange_list.append(exchange_info) - - output({ - "exchanges": exchange_list, - "total_exchanges": len(exchange_list), - "total_pairs": sum(ex["trading_pairs"] for ex in exchange_list) - }) - - -@exchange.command() -@click.argument("exchange_name") -@click.pass_context -def status(ctx, exchange_name: str): - """Get detailed status of a specific exchange""" - - # Load exchanges - exchanges_file = Path.home() / ".aitbc" / "exchanges.json" - if not exchanges_file.exists(): - error("No exchanges registered.") - return - - with open(exchanges_file, 'r') as f: - exchanges = json.load(f) - - if exchange_name.lower() not in exchanges: - error(f"Exchange '{exchange_name}' not found.") - return - - exchange_data = exchanges[exchange_name.lower()] - - output({ - "exchange": exchange_data["name"], - "status": exchange_data["status"], - "sandbox": exchange_data.get("sandbox", False), - "description": exchange_data.get("description"), - "created_at": exchange_data["created_at"], - "trading_pairs": exchange_data.get("trading_pairs", []), - "last_sync": exchange_data.get("last_sync") - }) - config = ctx.obj['config'] - - try: - with httpx.Client() as client: - response = client.get( - f"{config.coordinator_url}/v1/exchange/rates", - timeout=10 - ) - - if response.status_code == 200: - rates_data = response.json() - success("Current exchange rates:") - output(rates_data, ctx.obj['output_format']) - else: - error(f"Failed to get exchange rates: {response.status_code}") - except Exception as e: - error(f"Network error: {e}") - - -@exchange.command() -@click.option("--aitbc-amount", type=float, help="Amount of AITBC to buy") -@click.option("--btc-amount", type=float, help="Amount of BTC to spend") -@click.option("--user-id", help="User ID for the payment") -@click.option("--notes", help="Additional notes for the payment") -@click.pass_context -def create_payment(ctx, aitbc_amount: Optional[float], btc_amount: Optional[float], - user_id: Optional[str], notes: Optional[str]): - """Create a Bitcoin payment request for AITBC purchase""" - config = ctx.obj['config'] - - # Validate input - if aitbc_amount is not None and aitbc_amount <= 0: - error("AITBC amount must be greater than 0") - return - - if btc_amount is not None and btc_amount <= 0: - error("BTC amount must be greater than 0") - return - - if not aitbc_amount and not btc_amount: - error("Either --aitbc-amount or --btc-amount must be specified") - return - - # Get exchange rates to calculate missing amount - try: - with httpx.Client() as client: - rates_response = client.get( - f"{config.coordinator_url}/v1/exchange/rates", - timeout=10 - ) - - if rates_response.status_code != 200: - error("Failed to get exchange rates") - return - - rates = rates_response.json() - btc_to_aitbc = rates.get('btc_to_aitbc', 100000) - - # Calculate missing amount - if aitbc_amount and not btc_amount: - btc_amount = aitbc_amount / btc_to_aitbc - elif btc_amount and not aitbc_amount: - aitbc_amount = btc_amount * btc_to_aitbc - - # Prepare payment request - payment_data = { - "user_id": user_id or "cli_user", - "aitbc_amount": aitbc_amount, - "btc_amount": btc_amount - } - - if notes: - payment_data["notes"] = notes - - # Create payment - response = client.post( - f"{config.coordinator_url}/v1/exchange/create-payment", - json=payment_data, - timeout=10 - ) - - if response.status_code == 200: - payment = response.json() - success(f"Payment created: {payment.get('payment_id')}") - success(f"Send {btc_amount:.8f} BTC to: {payment.get('payment_address')}") - success(f"Expires at: {payment.get('expires_at')}") - output(payment, ctx.obj['output_format']) - else: - error(f"Failed to create payment: {response.status_code}") - if response.text: - error(f"Error details: {response.text}") - - except Exception as e: - error(f"Network error: {e}") - - -@exchange.command() -@click.option("--payment-id", required=True, help="Payment ID to check") -@click.pass_context -def payment_status(ctx, payment_id: str): - """Check payment confirmation status""" - config = ctx.obj['config'] - - try: - with httpx.Client() as client: - response = client.get( - f"{config.coordinator_url}/v1/exchange/payment-status/{payment_id}", - timeout=10 - ) - - if response.status_code == 200: - status_data = response.json() - status = status_data.get('status', 'unknown') - - if status == 'confirmed': - success(f"Payment {payment_id} is confirmed!") - success(f"AITBC amount: {status_data.get('aitbc_amount', 0)}") - elif status == 'pending': - success(f"Payment {payment_id} is pending confirmation") - elif status == 'expired': - error(f"Payment {payment_id} has expired") - else: - success(f"Payment {payment_id} status: {status}") - - output(status_data, ctx.obj['output_format']) - else: - error(f"Failed to get payment status: {response.status_code}") - except Exception as e: - error(f"Network error: {e}") - - -@exchange.command() -@click.pass_context -def market_stats(ctx): - """Get exchange market statistics""" - config = ctx.obj['config'] - - try: - with httpx.Client() as client: - response = client.get( - f"{config.coordinator_url}/v1/exchange/market-stats", - timeout=10 - ) - - if response.status_code == 200: - stats = response.json() - success("Exchange market statistics:") - output(stats, ctx.obj['output_format']) - else: - error(f"Failed to get market stats: {response.status_code}") - except Exception as e: - error(f"Network error: {e}") - - -@exchange.group() -def wallet(): - """Bitcoin wallet operations""" - pass - - -@wallet.command() -@click.pass_context -def balance(ctx): - """Get Bitcoin wallet balance""" - config = ctx.obj['config'] - - try: - with httpx.Client() as client: - response = client.get( - f"{config.coordinator_url}/exchange/wallet/balance", - timeout=10 - ) - - if response.status_code == 200: - balance_data = response.json() - success("Bitcoin wallet balance:") - output(balance_data, ctx.obj['output_format']) - else: - error(f"Failed to get wallet balance: {response.status_code}") - except Exception as e: - error(f"Network error: {e}") - - -@wallet.command() -@click.pass_context -def info(ctx): - """Get comprehensive Bitcoin wallet information""" - config = ctx.obj['config'] - - try: - with httpx.Client() as client: - response = client.get( - f"{config.coordinator_url}/exchange/wallet/info", - timeout=10 - ) - - if response.status_code == 200: - wallet_info = response.json() - success("Bitcoin wallet information:") - output(wallet_info, ctx.obj['output_format']) - else: - error(f"Failed to get wallet info: {response.status_code}") - except Exception as e: - error(f"Network error: {e}") - - -@exchange.command() -@click.option("--name", required=True, help="Exchange name (e.g., Binance, Coinbase)") -@click.option("--api-key", required=True, help="API key for exchange integration") -@click.option("--api-secret", help="API secret for exchange integration") -@click.option("--sandbox", is_flag=True, default=False, help="Use sandbox/testnet environment") -@click.pass_context -def register(ctx, name: str, api_key: str, api_secret: Optional[str], sandbox: bool): - """Register a new exchange integration""" - config = ctx.obj['config'] - - exchange_data = { - "name": name, - "api_key": api_key, - "sandbox": sandbox - } - - if api_secret: - exchange_data["api_secret"] = api_secret - - try: - with httpx.Client() as client: - response = client.post( - f"{config.coordinator_url}/v1/exchange/register", - json=exchange_data, - timeout=10 - ) - - if response.status_code == 200: - result = response.json() - success(f"Exchange '{name}' registered successfully!") - success(f"Exchange ID: {result.get('exchange_id')}") - output(result, ctx.obj['output_format']) - else: - error(f"Failed to register exchange: {response.status_code}") - if response.text: - error(f"Error details: {response.text}") - except Exception as e: - error(f"Network error: {e}") - - -@exchange.command() -@click.option("--pair", required=True, help="Trading pair (e.g., AITBC/BTC, AITBC/ETH)") -@click.option("--base-asset", required=True, help="Base asset symbol") -@click.option("--quote-asset", required=True, help="Quote asset symbol") -@click.option("--min-order-size", type=float, help="Minimum order size") -@click.option("--max-order-size", type=float, help="Maximum order size") -@click.option("--price-precision", type=int, default=8, help="Price decimal precision") -@click.option("--size-precision", type=int, default=8, help="Size decimal precision") -@click.pass_context -def create_pair(ctx, pair: str, base_asset: str, quote_asset: str, - min_order_size: Optional[float], max_order_size: Optional[float], - price_precision: int, size_precision: int): - """Create a new trading pair""" - config = ctx.obj['config'] - - pair_data = { - "pair": pair, - "base_asset": base_asset, - "quote_asset": quote_asset, - "price_precision": price_precision, - "size_precision": size_precision - } - - if min_order_size is not None: - pair_data["min_order_size"] = min_order_size - if max_order_size is not None: - pair_data["max_order_size"] = max_order_size - - try: - with httpx.Client() as client: - response = client.post( - f"{config.coordinator_url}/v1/exchange/create-pair", - json=pair_data, - timeout=10 - ) - - if response.status_code == 200: - result = response.json() - success(f"Trading pair '{pair}' created successfully!") - success(f"Pair ID: {result.get('pair_id')}") - output(result, ctx.obj['output_format']) - else: - error(f"Failed to create trading pair: {response.status_code}") - if response.text: - error(f"Error details: {response.text}") - except Exception as e: - error(f"Network error: {e}") - - -@exchange.command() -@click.option("--pair", required=True, help="Trading pair to start trading") -@click.option("--exchange", help="Specific exchange to enable") -@click.option("--order-type", multiple=True, default=["limit", "market"], - help="Order types to enable (limit, market, stop_limit)") -@click.pass_context -def start_trading(ctx, pair: str, exchange: Optional[str], order_type: tuple): - """Start trading for a specific pair""" - config = ctx.obj['config'] - - trading_data = { - "pair": pair, - "order_types": list(order_type) - } - - if exchange: - trading_data["exchange"] = exchange - - try: - with httpx.Client() as client: - response = client.post( - f"{config.coordinator_url}/v1/exchange/start-trading", - json=trading_data, - timeout=10 - ) - - if response.status_code == 200: - result = response.json() - success(f"Trading started for pair '{pair}'!") - success(f"Order types: {', '.join(order_type)}") - output(result, ctx.obj['output_format']) - else: - error(f"Failed to start trading: {response.status_code}") - if response.text: - error(f"Error details: {response.text}") - except Exception as e: - error(f"Network error: {e}") - - -@exchange.command() -@click.option("--pair", help="Filter by trading pair") -@click.option("--exchange", help="Filter by exchange") -@click.option("--status", help="Filter by status (active, inactive, suspended)") -@click.pass_context -def list_pairs(ctx, pair: Optional[str], exchange: Optional[str], status: Optional[str]): - """List all trading pairs""" - config = ctx.obj['config'] - - params = {} - if pair: - params["pair"] = pair - if exchange: - params["exchange"] = exchange - if status: - params["status"] = status - - try: - with httpx.Client() as client: - response = client.get( - f"{config.coordinator_url}/v1/exchange/pairs", - params=params, - timeout=10 - ) - - if response.status_code == 200: - pairs = response.json() - success("Trading pairs:") - output(pairs, ctx.obj['output_format']) - else: - error(f"Failed to list trading pairs: {response.status_code}") - except Exception as e: - error(f"Network error: {e}") - - -@exchange.command() -@click.option("--exchange", required=True, help="Exchange name (binance, coinbasepro, kraken)") -@click.option("--api-key", required=True, help="API key for exchange") -@click.option("--secret", required=True, help="API secret for exchange") -@click.option("--sandbox", is_flag=True, default=True, help="Use sandbox/testnet environment") -@click.option("--passphrase", help="API passphrase (for Coinbase)") -@click.pass_context -def connect(ctx, exchange: str, api_key: str, secret: str, sandbox: bool, passphrase: Optional[str]): - """Connect to a real exchange API""" - try: - # Import the real exchange integration - import sys - sys.path.append('/home/oib/windsurf/aitbc/apps/exchange') - from real_exchange_integration import connect_to_exchange - - # Run async connection - import asyncio - success = asyncio.run(connect_to_exchange(exchange, api_key, secret, sandbox, passphrase)) - - if success: - success(f"✅ Successfully connected to {exchange}") - if sandbox: - success("🧪 Using sandbox/testnet environment") - else: - error(f"❌ Failed to connect to {exchange}") - - except ImportError: - error("❌ Real exchange integration not available. Install ccxt library.") - except Exception as e: - error(f"❌ Connection error: {e}") - - -@exchange.command() -@click.option("--exchange", help="Check specific exchange (default: all)") -@click.pass_context -def status(ctx, exchange: Optional[str]): - """Check exchange connection status""" - try: - # Import the real exchange integration - import sys - sys.path.append('/home/oib/windsurf/aitbc/apps/exchange') - from real_exchange_integration import get_exchange_status - - # Run async status check - import asyncio - status_data = asyncio.run(get_exchange_status(exchange)) - - # Display status - for exchange_name, health in status_data.items(): - status_icon = "🟢" if health.status.value == "connected" else "🔴" if health.status.value == "error" else "🟡" - - success(f"{status_icon} {exchange_name.upper()}") - success(f" Status: {health.status.value}") - success(f" Latency: {health.latency_ms:.2f}ms") - success(f" Last Check: {health.last_check.strftime('%H:%M:%S')}") - - if health.error_message: - error(f" Error: {health.error_message}") - print() - - except ImportError: - error("❌ Real exchange integration not available. Install ccxt library.") - except Exception as e: - error(f"❌ Status check error: {e}") - - -@exchange.command() -@click.option("--exchange", required=True, help="Exchange name to disconnect") -@click.pass_context -def disconnect(ctx, exchange: str): - """Disconnect from an exchange""" - try: - # Import the real exchange integration - import sys - sys.path.append('/home/oib/windsurf/aitbc/apps/exchange') - from real_exchange_integration import disconnect_from_exchange - - # Run async disconnection - import asyncio - success = asyncio.run(disconnect_from_exchange(exchange)) - - if success: - success(f"🔌 Disconnected from {exchange}") - else: - error(f"❌ Failed to disconnect from {exchange}") - - except ImportError: - error("❌ Real exchange integration not available. Install ccxt library.") - except Exception as e: - error(f"❌ Disconnection error: {e}") - - -@exchange.command() -@click.option("--exchange", required=True, help="Exchange name") -@click.option("--symbol", required=True, help="Trading symbol (e.g., BTC/USDT)") -@click.option("--limit", type=int, default=20, help="Order book depth") -@click.pass_context -def orderbook(ctx, exchange: str, symbol: str, limit: int): - """Get order book from exchange""" - try: - # Import the real exchange integration - import sys - sys.path.append('/home/oib/windsurf/aitbc/apps/exchange') - from real_exchange_integration import exchange_manager - - # Run async order book fetch - import asyncio - orderbook = asyncio.run(exchange_manager.get_order_book(exchange, symbol, limit)) - - # Display order book - success(f"📊 Order Book for {symbol} on {exchange.upper()}") - - # Display bids (buy orders) - if 'bids' in orderbook and orderbook['bids']: - success("\n🟢 Bids (Buy Orders):") - for i, bid in enumerate(orderbook['bids'][:10]): - price, amount = bid - success(f" {i+1}. ${price:.8f} x {amount:.6f}") - - # Display asks (sell orders) - if 'asks' in orderbook and orderbook['asks']: - success("\n🔴 Asks (Sell Orders):") - for i, ask in enumerate(orderbook['asks'][:10]): - price, amount = ask - success(f" {i+1}. ${price:.8f} x {amount:.6f}") - - # Spread - if 'bids' in orderbook and 'asks' in orderbook and orderbook['bids'] and orderbook['asks']: - best_bid = orderbook['bids'][0][0] - best_ask = orderbook['asks'][0][0] - spread = best_ask - best_bid - spread_pct = (spread / best_bid) * 100 - - success(f"\n📈 Spread: ${spread:.8f} ({spread_pct:.4f}%)") - success(f"🎯 Best Bid: ${best_bid:.8f}") - success(f"🎯 Best Ask: ${best_ask:.8f}") - - except ImportError: - error("❌ Real exchange integration not available. Install ccxt library.") - except Exception as e: - error(f"❌ Order book error: {e}") - - -@exchange.command() -@click.option("--exchange", required=True, help="Exchange name") -@click.pass_context -def balance(ctx, exchange: str): - """Get account balance from exchange""" - try: - # Import the real exchange integration - import sys - sys.path.append('/home/oib/windsurf/aitbc/apps/exchange') - from real_exchange_integration import exchange_manager - - # Run async balance fetch - import asyncio - balance_data = asyncio.run(exchange_manager.get_balance(exchange)) - - # Display balance - success(f"💰 Account Balance on {exchange.upper()}") - - if 'total' in balance_data: - for asset, amount in balance_data['total'].items(): - if amount > 0: - available = balance_data.get('free', {}).get(asset, 0) - used = balance_data.get('used', {}).get(asset, 0) - - success(f"\n{asset}:") - success(f" Total: {amount:.8f}") - success(f" Available: {available:.8f}") - success(f" In Orders: {used:.8f}") - else: - warning("No balance data available") - - except ImportError: - error("❌ Real exchange integration not available. Install ccxt library.") - except Exception as e: - error(f"❌ Balance error: {e}") - - -@exchange.command() -@click.option("--exchange", required=True, help="Exchange name") -@click.pass_context -def pairs(ctx, exchange: str): - """List supported trading pairs""" - try: - # Import the real exchange integration - import sys - sys.path.append('/home/oib/windsurf/aitbc/apps/exchange') - from real_exchange_integration import exchange_manager - - # Run async pairs fetch - import asyncio - pairs = asyncio.run(exchange_manager.get_supported_pairs(exchange)) - - # Display pairs - success(f"📋 Supported Trading Pairs on {exchange.upper()}") - success(f"Found {len(pairs)} trading pairs:\n") - - # Group by base currency - base_currencies = {} - for pair in pairs: - base = pair.split('/')[0] if '/' in pair else pair.split('-')[0] - if base not in base_currencies: - base_currencies[base] = [] - base_currencies[base].append(pair) - - # Display organized pairs - for base in sorted(base_currencies.keys()): - success(f"\n🔹 {base}:") - for pair in sorted(base_currencies[base][:10]): # Show first 10 per base - success(f" • {pair}") - - if len(base_currencies[base]) > 10: - success(f" ... and {len(base_currencies[base]) - 10} more") - - except ImportError: - error("❌ Real exchange integration not available. Install ccxt library.") - except Exception as e: - error(f"❌ Pairs error: {e}") - - -@exchange.command() -@click.pass_context -def list_exchanges(ctx): - """List all supported exchanges""" - try: - # Import the real exchange integration - import sys - sys.path.append('/home/oib/windsurf/aitbc/apps/exchange') - from real_exchange_integration import exchange_manager - - success("🏢 Supported Exchanges:") - for exchange in exchange_manager.supported_exchanges: - success(f" • {exchange.title()}") - - success("\n📝 Usage:") - success(" aitbc exchange connect --exchange binance --api-key --secret ") - success(" aitbc exchange status --exchange binance") - success(" aitbc exchange orderbook --exchange binance --symbol BTC/USDT") - - except ImportError: - error("❌ Real exchange integration not available. Install ccxt library.") - except Exception as e: - error(f"❌ Error: {e}") diff --git a/cli/aitbc_cli/commands/marketplace_cmd.py.bak b/cli/aitbc_cli/commands/marketplace_cmd.py.bak deleted file mode 100755 index e3f25266..00000000 --- a/cli/aitbc_cli/commands/marketplace_cmd.py.bak +++ /dev/null @@ -1,494 +0,0 @@ -"""Global chain marketplace commands for AITBC CLI""" - -import click -import asyncio -import json -from decimal import Decimal -from datetime import datetime -from typing import Optional -from ..core.config import load_multichain_config -from ..core.marketplace import ( - GlobalChainMarketplace, ChainType, MarketplaceStatus, - TransactionStatus -) -from ..utils import output, error, success - -@click.group() -def marketplace(): - """Global chain marketplace commands""" - pass - -@marketplace.command() -@click.argument('chain_id') -@click.argument('chain_name') -@click.argument('chain_type') -@click.argument('description') -@click.argument('seller_id') -@click.argument('price') -@click.option('--currency', default='ETH', help='Currency for pricing') -@click.option('--specs', help='Chain specifications (JSON string)') -@click.option('--metadata', help='Additional metadata (JSON string)') -@click.pass_context -def list(ctx, chain_id, chain_name, chain_type, description, seller_id, price, currency, specs, metadata): - """List a chain for sale in the marketplace""" - try: - config = load_multichain_config() - marketplace = GlobalChainMarketplace(config) - - # Parse chain type - try: - chain_type_enum = ChainType(chain_type) - except ValueError: - error(f"Invalid chain type: {chain_type}") - error(f"Valid types: {[t.value for t in ChainType]}") - raise click.Abort() - - # Parse price - try: - price_decimal = Decimal(price) - except: - error("Invalid price format") - raise click.Abort() - - # Parse specifications - chain_specs = {} - if specs: - try: - chain_specs = json.loads(specs) - except json.JSONDecodeError: - error("Invalid JSON specifications") - raise click.Abort() - - # Parse metadata - metadata_dict = {} - if metadata: - try: - metadata_dict = json.loads(metadata) - except json.JSONDecodeError: - error("Invalid JSON metadata") - raise click.Abort() - - # Create listing - listing_id = asyncio.run(marketplace.create_listing( - chain_id, chain_name, chain_type_enum, description, - seller_id, price_decimal, currency, chain_specs, metadata_dict - )) - - if listing_id: - success(f"Chain listed successfully! Listing ID: {listing_id}") - - listing_data = { - "Listing ID": listing_id, - "Chain ID": chain_id, - "Chain Name": chain_name, - "Type": chain_type, - "Price": f"{price} {currency}", - "Seller": seller_id, - "Status": "active", - "Created": datetime.now().strftime("%Y-%m-%d %H:%M:%S") - } - - output(listing_data, ctx.obj.get('output_format', 'table')) - else: - error("Failed to create listing") - raise click.Abort() - - except Exception as e: - error(f"Error creating listing: {str(e)}") - raise click.Abort() - -@marketplace.command() -@click.argument('listing_id') -@click.argument('buyer_id') -@click.option('--payment', default='crypto', help='Payment method') -@click.pass_context -def buy(ctx, listing_id, buyer_id, payment): - """Purchase a chain from the marketplace""" - try: - config = load_multichain_config() - marketplace = GlobalChainMarketplace(config) - - # Purchase chain - transaction_id = asyncio.run(marketplace.purchase_chain(listing_id, buyer_id, payment)) - - if transaction_id: - success(f"Purchase initiated! Transaction ID: {transaction_id}") - - transaction_data = { - "Transaction ID": transaction_id, - "Listing ID": listing_id, - "Buyer": buyer_id, - "Payment Method": payment, - "Status": "pending", - "Created": datetime.now().strftime("%Y-%m-%d %H:%M:%S") - } - - output(transaction_data, ctx.obj.get('output_format', 'table')) - else: - error("Failed to purchase chain") - raise click.Abort() - - except Exception as e: - error(f"Error purchasing chain: {str(e)}") - raise click.Abort() - -@marketplace.command() -@click.argument('transaction_id') -@click.argument('transaction_hash') -@click.pass_context -def complete(ctx, transaction_id, transaction_hash): - """Complete a marketplace transaction""" - try: - config = load_multichain_config() - marketplace = GlobalChainMarketplace(config) - - # Complete transaction - success = asyncio.run(marketplace.complete_transaction(transaction_id, transaction_hash)) - - if success: - success(f"Transaction {transaction_id} completed successfully!") - - transaction_data = { - "Transaction ID": transaction_id, - "Transaction Hash": transaction_hash, - "Status": "completed", - "Completed": datetime.now().strftime("%Y-%m-%d %H:%M:%S") - } - - output(transaction_data, ctx.obj.get('output_format', 'table')) - else: - error(f"Failed to complete transaction {transaction_id}") - raise click.Abort() - - except Exception as e: - error(f"Error completing transaction: {str(e)}") - raise click.Abort() - -@marketplace.command() -@click.option('--type', help='Filter by chain type') -@click.option('--min-price', help='Minimum price') -@click.option('--max-price', help='Maximum price') -@click.option('--seller', help='Filter by seller ID') -@click.option('--status', help='Filter by listing status') -@click.option('--format', type=click.Choice(['table', 'json']), default='table', help='Output format') -@click.pass_context -def search(ctx, type, min_price, max_price, seller, status, format): - """Search chain listings in the marketplace""" - try: - config = load_multichain_config() - marketplace = GlobalChainMarketplace(config) - - # Parse filters - chain_type = None - if type: - try: - chain_type = ChainType(type) - except ValueError: - error(f"Invalid chain type: {type}") - raise click.Abort() - - min_price_dec = None - if min_price: - try: - min_price_dec = Decimal(min_price) - except: - error("Invalid minimum price format") - raise click.Abort() - - max_price_dec = None - if max_price: - try: - max_price_dec = Decimal(max_price) - except: - error("Invalid maximum price format") - raise click.Abort() - - listing_status = None - if status: - try: - listing_status = MarketplaceStatus(status) - except ValueError: - error(f"Invalid status: {status}") - raise click.Abort() - - # Search listings - listings = asyncio.run(marketplace.search_listings( - chain_type, min_price_dec, max_price_dec, seller, listing_status - )) - - if not listings: - output("No listings found matching your criteria", ctx.obj.get('output_format', 'table')) - return - - # Format output - listing_data = [ - { - "Listing ID": listing.listing_id, - "Chain ID": listing.chain_id, - "Chain Name": listing.chain_name, - "Type": listing.chain_type.value, - "Price": f"{listing.price} {listing.currency}", - "Seller": listing.seller_id, - "Status": listing.status.value, - "Created": listing.created_at.strftime("%Y-%m-%d %H:%M:%S"), - "Expires": listing.expires_at.strftime("%Y-%m-%d %H:%M:%S") - } - for listing in listings - ] - - output(listing_data, ctx.obj.get('output_format', format), title="Marketplace Listings") - - except Exception as e: - error(f"Error searching listings: {str(e)}") - raise click.Abort() - -@marketplace.command() -@click.argument('chain_id') -@click.option('--format', type=click.Choice(['table', 'json']), default='table', help='Output format') -@click.pass_context -def economy(ctx, chain_id, format): - """Get economic metrics for a specific chain""" - try: - config = load_multichain_config() - marketplace = GlobalChainMarketplace(config) - - # Get chain economy - economy = asyncio.run(marketplace.get_chain_economy(chain_id)) - - if not economy: - error(f"No economic data available for chain {chain_id}") - raise click.Abort() - - # Format output - economy_data = [ - {"Metric": "Chain ID", "Value": economy.chain_id}, - {"Metric": "Total Value Locked", "Value": f"{economy.total_value_locked} ETH"}, - {"Metric": "Daily Volume", "Value": f"{economy.daily_volume} ETH"}, - {"Metric": "Market Cap", "Value": f"{economy.market_cap} ETH"}, - {"Metric": "Transaction Count", "Value": economy.transaction_count}, - {"Metric": "Active Users", "Value": economy.active_users}, - {"Metric": "Agent Count", "Value": economy.agent_count}, - {"Metric": "Governance Tokens", "Value": f"{economy.governance_tokens}"}, - {"Metric": "Staking Rewards", "Value": f"{economy.staking_rewards}"}, - {"Metric": "Last Updated", "Value": economy.last_updated.strftime("%Y-%m-%d %H:%M:%S")} - ] - - output(economy_data, ctx.obj.get('output_format', format), title=f"Chain Economy: {chain_id}") - - except Exception as e: - error(f"Error getting chain economy: {str(e)}") - raise click.Abort() - -@marketplace.command() -@click.argument('user_id') -@click.option('--role', type=click.Choice(['buyer', 'seller', 'both']), default='both', help='User role') -@click.option('--format', type=click.Choice(['table', 'json']), default='table', help='Output format') -@click.pass_context -def transactions(ctx, user_id, role, format): - """Get transactions for a specific user""" - try: - config = load_multichain_config() - marketplace = GlobalChainMarketplace(config) - - # Get user transactions - transactions = asyncio.run(marketplace.get_user_transactions(user_id, role)) - - if not transactions: - output(f"No transactions found for user {user_id}", ctx.obj.get('output_format', 'table')) - return - - # Format output - transaction_data = [ - { - "Transaction ID": transaction.transaction_id, - "Listing ID": transaction.listing_id, - "Chain ID": transaction.chain_id, - "Price": f"{transaction.price} {transaction.currency}", - "Role": "buyer" if transaction.buyer_id == user_id else "seller", - "Counterparty": transaction.seller_id if transaction.buyer_id == user_id else transaction.buyer_id, - "Status": transaction.status.value, - "Created": transaction.created_at.strftime("%Y-%m-%d %H:%M:%S"), - "Completed": transaction.completed_at.strftime("%Y-%m-%d %H:%M:%S") if transaction.completed_at else "N/A" - } - for transaction in transactions - ] - - output(transaction_data, ctx.obj.get('output_format', format), title=f"Transactions for {user_id}") - - except Exception as e: - error(f"Error getting user transactions: {str(e)}") - raise click.Abort() - -@marketplace.command() -@click.option('--format', type=click.Choice(['table', 'json']), default='table', help='Output format') -@click.pass_context -def overview(ctx, format): - """Get comprehensive marketplace overview""" - try: - config = load_multichain_config() - marketplace = GlobalChainMarketplace(config) - - # Get marketplace overview - overview = asyncio.run(marketplace.get_marketplace_overview()) - - if not overview: - error("No marketplace data available") - raise click.Abort() - - # Marketplace metrics - if "marketplace_metrics" in overview: - metrics = overview["marketplace_metrics"] - metrics_data = [ - {"Metric": "Total Listings", "Value": metrics["total_listings"]}, - {"Metric": "Active Listings", "Value": metrics["active_listings"]}, - {"Metric": "Total Transactions", "Value": metrics["total_transactions"]}, - {"Metric": "Total Volume", "Value": f"{metrics['total_volume']} ETH"}, - {"Metric": "Average Price", "Value": f"{metrics['average_price']} ETH"}, - {"Metric": "Market Sentiment", "Value": f"{metrics['market_sentiment']:.2f}"} - ] - - output(metrics_data, ctx.obj.get('output_format', format), title="Marketplace Metrics") - - # Volume 24h - if "volume_24h" in overview: - volume_data = [ - {"Metric": "24h Volume", "Value": f"{overview['volume_24h']} ETH"} - ] - - output(volume_data, ctx.obj.get('output_format', format), title="24-Hour Volume") - - # Top performing chains - if "top_performing_chains" in overview: - chains = overview["top_performing_chains"] - if chains: - chain_data = [ - { - "Chain ID": chain["chain_id"], - "Volume": f"{chain['volume']} ETH", - "Transactions": chain["transactions"] - } - for chain in chains[:5] # Top 5 - ] - - output(chain_data, ctx.obj.get('output_format', format), title="Top Performing Chains") - - # Chain types distribution - if "chain_types_distribution" in overview: - distribution = overview["chain_types_distribution"] - if distribution: - dist_data = [ - {"Chain Type": chain_type, "Count": count} - for chain_type, count in distribution.items() - ] - - output(dist_data, ctx.obj.get('output_format', format), title="Chain Types Distribution") - - # User activity - if "user_activity" in overview: - activity = overview["user_activity"] - activity_data = [ - {"Metric": "Active Buyers (7d)", "Value": activity["active_buyers_7d"]}, - {"Metric": "Active Sellers (7d)", "Value": activity["active_sellers_7d"]}, - {"Metric": "Total Unique Users", "Value": activity["total_unique_users"]}, - {"Metric": "Average Reputation", "Value": f"{activity['average_reputation']:.3f}"} - ] - - output(activity_data, ctx.obj.get('output_format', format), title="User Activity") - - # Escrow summary - if "escrow_summary" in overview: - escrow = overview["escrow_summary"] - escrow_data = [ - {"Metric": "Active Escrows", "Value": escrow["active_escrows"]}, - {"Metric": "Released Escrows", "Value": escrow["released_escrows"]}, - {"Metric": "Total Escrow Value", "Value": f"{escrow['total_escrow_value']} ETH"}, - {"Metric": "Escrow Fees Collected", "Value": f"{escrow['escrow_fee_collected']} ETH"} - ] - - output(escrow_data, ctx.obj.get('output_format', format), title="Escrow Summary") - - except Exception as e: - error(f"Error getting marketplace overview: {str(e)}") - raise click.Abort() - -@marketplace.command() -@click.option('--realtime', is_flag=True, help='Real-time monitoring') -@click.option('--interval', default=30, help='Update interval in seconds') -@click.pass_context -def monitor(ctx, realtime, interval): - """Monitor marketplace activity""" - try: - config = load_multichain_config() - marketplace = GlobalChainMarketplace(config) - - if realtime: - # Real-time monitoring - from rich.console import Console - from rich.live import Live - from rich.table import Table - import time - - console = Console() - - def generate_monitor_table(): - try: - overview = asyncio.run(marketplace.get_marketplace_overview()) - - table = Table(title=f"Marketplace Monitor - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") - table.add_column("Metric", style="cyan") - table.add_column("Value", style="green") - - if "marketplace_metrics" in overview: - metrics = overview["marketplace_metrics"] - table.add_row("Total Listings", str(metrics["total_listings"])) - table.add_row("Active Listings", str(metrics["active_listings"])) - table.add_row("Total Transactions", str(metrics["total_transactions"])) - table.add_row("Total Volume", f"{metrics['total_volume']} ETH") - table.add_row("Market Sentiment", f"{metrics['market_sentiment']:.2f}") - - if "volume_24h" in overview: - table.add_row("24h Volume", f"{overview['volume_24h']} ETH") - - if "user_activity" in overview: - activity = overview["user_activity"] - table.add_row("Active Users (7d)", str(activity["active_buyers_7d"] + activity["active_sellers_7d"])) - - return table - except Exception as e: - return f"Error getting marketplace data: {e}" - - with Live(generate_monitor_table(), refresh_per_second=1) as live: - try: - while True: - live.update(generate_monitor_table()) - time.sleep(interval) - except KeyboardInterrupt: - console.print("\n[yellow]Monitoring stopped by user[/yellow]") - else: - # Single snapshot - overview = asyncio.run(marketplace.get_marketplace_overview()) - - monitor_data = [] - - if "marketplace_metrics" in overview: - metrics = overview["marketplace_metrics"] - monitor_data.extend([ - {"Metric": "Total Listings", "Value": metrics["total_listings"]}, - {"Metric": "Active Listings", "Value": metrics["active_listings"]}, - {"Metric": "Total Transactions", "Value": metrics["total_transactions"]}, - {"Metric": "Total Volume", "Value": f"{metrics['total_volume']} ETH"}, - {"Metric": "Market Sentiment", "Value": f"{metrics['market_sentiment']:.2f}"} - ]) - - if "volume_24h" in overview: - monitor_data.append({"Metric": "24h Volume", "Value": f"{overview['volume_24h']} ETH"}) - - if "user_activity" in overview: - activity = overview["user_activity"] - monitor_data.append({"Metric": "Active Users (7d)", "Value": activity["active_buyers_7d"] + activity["active_sellers_7d"]}) - - output(monitor_data, ctx.obj.get('output_format', 'table'), title="Marketplace Monitor") - - except Exception as e: - error(f"Error during monitoring: {str(e)}") - raise click.Abort() diff --git a/cli/aitbc_cli/commands/monitor.py.bak b/cli/aitbc_cli/commands/monitor.py.bak deleted file mode 100755 index 79972f9a..00000000 --- a/cli/aitbc_cli/commands/monitor.py.bak +++ /dev/null @@ -1,485 +0,0 @@ -"""Monitoring and dashboard commands for AITBC CLI""" - -import click -import httpx -import json -import time -from pathlib import Path -from typing import Optional -from datetime import datetime, timedelta -from ..utils import output, error, success, console - - -@click.group() -def monitor(): - """Monitoring, metrics, and alerting commands""" - pass - - -@monitor.command() -@click.option("--refresh", type=int, default=5, help="Refresh interval in seconds") -@click.option("--duration", type=int, default=0, help="Duration in seconds (0 = indefinite)") -@click.pass_context -def dashboard(ctx, refresh: int, duration: int): - """Real-time system dashboard""" - config = ctx.obj['config'] - start_time = time.time() - - try: - while True: - elapsed = time.time() - start_time - if duration > 0 and elapsed >= duration: - break - - console.clear() - console.rule("[bold blue]AITBC Dashboard[/bold blue]") - console.print(f"[dim]Refreshing every {refresh}s | Elapsed: {int(elapsed)}s[/dim]\n") - - # Fetch system dashboard - try: - with httpx.Client(timeout=5) as client: - # Get dashboard data - try: - url = f"{config.coordinator_url}/api/v1/dashboard" - resp = client.get( - url, - headers={"X-Api-Key": config.api_key or ""} - ) - if resp.status_code == 200: - dashboard = resp.json() - console.print("[bold green]Dashboard Status:[/bold green] Online") - - # Overall status - overall_status = dashboard.get("overall_status", "unknown") - console.print(f" Overall Status: {overall_status}") - - # Services summary - services = dashboard.get("services", {}) - console.print(f" Services: {len(services)}") - - for service_name, service_data in services.items(): - status = service_data.get("status", "unknown") - console.print(f" {service_name}: {status}") - - # Metrics summary - metrics = dashboard.get("metrics", {}) - if metrics: - health_pct = metrics.get("health_percentage", 0) - console.print(f" Health: {health_pct:.1f}%") - - else: - console.print(f"[bold yellow]Dashboard:[/bold yellow] HTTP {resp.status_code}") - except Exception as e: - console.print(f"[bold red]Dashboard:[/bold red] Error - {e}") - - except Exception as e: - console.print(f"[red]Error fetching data: {e}[/red]") - - console.print(f"\n[dim]Press Ctrl+C to exit[/dim]") - time.sleep(refresh) - - except KeyboardInterrupt: - console.print("\n[bold]Dashboard stopped[/bold]") - - -@monitor.command() -@click.option("--period", default="24h", help="Time period (1h, 24h, 7d, 30d)") -@click.option("--export", "export_path", type=click.Path(), help="Export metrics to file") -@click.pass_context -def metrics(ctx, period: str, export_path: Optional[str]): - """Collect and display system metrics""" - config = ctx.obj['config'] - - # Parse period - multipliers = {"h": 3600, "d": 86400} - unit = period[-1] - value = int(period[:-1]) - seconds = value * multipliers.get(unit, 3600) - since = datetime.now() - timedelta(seconds=seconds) - - metrics_data = { - "period": period, - "since": since.isoformat(), - "collected_at": datetime.now().isoformat(), - "coordinator": {}, - "jobs": {}, - "miners": {} - } - - try: - with httpx.Client(timeout=10) as client: - # Coordinator metrics - try: - resp = client.get( - f"{config.coordinator_url}/status", - headers={"X-Api-Key": config.api_key or ""} - ) - if resp.status_code == 200: - metrics_data["coordinator"] = resp.json() - metrics_data["coordinator"]["status"] = "online" - else: - metrics_data["coordinator"]["status"] = f"error_{resp.status_code}" - except Exception: - metrics_data["coordinator"]["status"] = "offline" - - # Job metrics - try: - resp = client.get( - f"{config.coordinator_url}/jobs", - headers={"X-Api-Key": config.api_key or ""}, - params={"limit": 100} - ) - if resp.status_code == 200: - jobs = resp.json() - if isinstance(jobs, list): - metrics_data["jobs"] = { - "total": len(jobs), - "completed": sum(1 for j in jobs if j.get("status") == "completed"), - "pending": sum(1 for j in jobs if j.get("status") == "pending"), - "failed": sum(1 for j in jobs if j.get("status") == "failed"), - } - except Exception: - metrics_data["jobs"] = {"error": "unavailable"} - - # Miner metrics - try: - resp = client.get( - f"{config.coordinator_url}/miners", - headers={"X-Api-Key": config.api_key or ""} - ) - if resp.status_code == 200: - miners = resp.json() - if isinstance(miners, list): - metrics_data["miners"] = { - "total": len(miners), - "online": sum(1 for m in miners if m.get("status") == "ONLINE"), - "offline": sum(1 for m in miners if m.get("status") != "ONLINE"), - } - except Exception: - metrics_data["miners"] = {"error": "unavailable"} - - except Exception as e: - error(f"Failed to collect metrics: {e}") - - if export_path: - with open(export_path, "w") as f: - json.dump(metrics_data, f, indent=2) - success(f"Metrics exported to {export_path}") - - output(metrics_data, ctx.obj['output_format']) - - -@monitor.command() -@click.argument("action", type=click.Choice(["add", "list", "remove", "test"])) -@click.option("--name", help="Alert name") -@click.option("--type", "alert_type", type=click.Choice(["coordinator_down", "miner_offline", "job_failed", "low_balance"]), help="Alert type") -@click.option("--threshold", type=float, help="Alert threshold value") -@click.option("--webhook", help="Webhook URL for notifications") -@click.pass_context -def alerts(ctx, action: str, name: Optional[str], alert_type: Optional[str], - threshold: Optional[float], webhook: Optional[str]): - """Configure monitoring alerts""" - alerts_dir = Path.home() / ".aitbc" / "alerts" - alerts_dir.mkdir(parents=True, exist_ok=True) - alerts_file = alerts_dir / "alerts.json" - - # Load existing alerts - existing = [] - if alerts_file.exists(): - with open(alerts_file) as f: - existing = json.load(f) - - if action == "add": - if not name or not alert_type: - error("Alert name and type required (--name, --type)") - return - alert = { - "name": name, - "type": alert_type, - "threshold": threshold, - "webhook": webhook, - "created_at": datetime.now().isoformat(), - "enabled": True - } - existing.append(alert) - with open(alerts_file, "w") as f: - json.dump(existing, f, indent=2) - success(f"Alert '{name}' added") - output(alert, ctx.obj['output_format']) - - elif action == "list": - if not existing: - output({"message": "No alerts configured"}, ctx.obj['output_format']) - else: - output(existing, ctx.obj['output_format']) - - elif action == "remove": - if not name: - error("Alert name required (--name)") - return - existing = [a for a in existing if a["name"] != name] - with open(alerts_file, "w") as f: - json.dump(existing, f, indent=2) - success(f"Alert '{name}' removed") - - elif action == "test": - if not name: - error("Alert name required (--name)") - return - alert = next((a for a in existing if a["name"] == name), None) - if not alert: - error(f"Alert '{name}' not found") - return - if alert.get("webhook"): - try: - with httpx.Client(timeout=10) as client: - resp = client.post(alert["webhook"], json={ - "alert": name, - "type": alert["type"], - "message": f"Test alert from AITBC CLI", - "timestamp": datetime.now().isoformat() - }) - output({"status": "sent", "response_code": resp.status_code}, ctx.obj['output_format']) - except Exception as e: - error(f"Webhook test failed: {e}") - else: - output({"status": "no_webhook", "alert": alert}, ctx.obj['output_format']) - - -@monitor.command() -@click.option("--period", default="7d", help="Analysis period (1d, 7d, 30d)") -@click.pass_context -def history(ctx, period: str): - """Historical data analysis""" - config = ctx.obj['config'] - - multipliers = {"h": 3600, "d": 86400} - unit = period[-1] - value = int(period[:-1]) - seconds = value * multipliers.get(unit, 3600) - since = datetime.now() - timedelta(seconds=seconds) - - analysis = { - "period": period, - "since": since.isoformat(), - "analyzed_at": datetime.now().isoformat(), - "summary": {} - } - - try: - with httpx.Client(timeout=10) as client: - try: - resp = client.get( - f"{config.coordinator_url}/jobs", - headers={"X-Api-Key": config.api_key or ""}, - params={"limit": 500} - ) - if resp.status_code == 200: - jobs = resp.json() - if isinstance(jobs, list): - completed = [j for j in jobs if j.get("status") == "completed"] - failed = [j for j in jobs if j.get("status") == "failed"] - analysis["summary"] = { - "total_jobs": len(jobs), - "completed": len(completed), - "failed": len(failed), - "success_rate": f"{len(completed) / max(1, len(jobs)) * 100:.1f}%", - } - except Exception: - analysis["summary"] = {"error": "Could not fetch job data"} - - except Exception as e: - error(f"Analysis failed: {e}") - - output(analysis, ctx.obj['output_format']) - - -@monitor.command() -@click.argument("action", type=click.Choice(["add", "list", "remove", "test"])) -@click.option("--name", help="Webhook name") -@click.option("--url", help="Webhook URL") -@click.option("--events", help="Comma-separated event types (job_completed,miner_offline,alert)") -@click.pass_context -def webhooks(ctx, action: str, name: Optional[str], url: Optional[str], events: Optional[str]): - """Manage webhook notifications""" - webhooks_dir = Path.home() / ".aitbc" / "webhooks" - webhooks_dir.mkdir(parents=True, exist_ok=True) - webhooks_file = webhooks_dir / "webhooks.json" - - existing = [] - if webhooks_file.exists(): - with open(webhooks_file) as f: - existing = json.load(f) - - if action == "add": - if not name or not url: - error("Webhook name and URL required (--name, --url)") - return - webhook = { - "name": name, - "url": url, - "events": events.split(",") if events else ["all"], - "created_at": datetime.now().isoformat(), - "enabled": True - } - existing.append(webhook) - with open(webhooks_file, "w") as f: - json.dump(existing, f, indent=2) - success(f"Webhook '{name}' added") - output(webhook, ctx.obj['output_format']) - - elif action == "list": - if not existing: - output({"message": "No webhooks configured"}, ctx.obj['output_format']) - else: - output(existing, ctx.obj['output_format']) - - elif action == "remove": - if not name: - error("Webhook name required (--name)") - return - existing = [w for w in existing if w["name"] != name] - with open(webhooks_file, "w") as f: - json.dump(existing, f, indent=2) - success(f"Webhook '{name}' removed") - - elif action == "test": - if not name: - error("Webhook name required (--name)") - return - wh = next((w for w in existing if w["name"] == name), None) - if not wh: - error(f"Webhook '{name}' not found") - return - try: - with httpx.Client(timeout=10) as client: - resp = client.post(wh["url"], json={ - "event": "test", - "source": "aitbc-cli", - "message": "Test webhook notification", - "timestamp": datetime.now().isoformat() - }) - output({"status": "sent", "response_code": resp.status_code}, ctx.obj['output_format']) - except Exception as e: - error(f"Webhook test failed: {e}") - - -CAMPAIGNS_DIR = Path.home() / ".aitbc" / "campaigns" - - -def _ensure_campaigns(): - CAMPAIGNS_DIR.mkdir(parents=True, exist_ok=True) - campaigns_file = CAMPAIGNS_DIR / "campaigns.json" - if not campaigns_file.exists(): - # Seed with default campaigns - default = {"campaigns": [ - { - "id": "staking_launch", - "name": "Staking Launch Campaign", - "type": "staking", - "apy_boost": 2.0, - "start_date": "2026-02-01T00:00:00", - "end_date": "2026-04-01T00:00:00", - "status": "active", - "total_staked": 0, - "participants": 0, - "rewards_distributed": 0 - }, - { - "id": "liquidity_mining_q1", - "name": "Q1 Liquidity Mining", - "type": "liquidity", - "apy_boost": 3.0, - "start_date": "2026-01-15T00:00:00", - "end_date": "2026-03-15T00:00:00", - "status": "active", - "total_staked": 0, - "participants": 0, - "rewards_distributed": 0 - } - ]} - with open(campaigns_file, "w") as f: - json.dump(default, f, indent=2) - return campaigns_file - - -@monitor.command() -@click.option("--status", type=click.Choice(["active", "ended", "all"]), default="all", help="Filter by status") -@click.pass_context -def campaigns(ctx, status: str): - """List active incentive campaigns""" - campaigns_file = _ensure_campaigns() - with open(campaigns_file) as f: - data = json.load(f) - - campaign_list = data.get("campaigns", []) - - # Auto-update status - now = datetime.now() - for c in campaign_list: - end = datetime.fromisoformat(c["end_date"]) - if now > end and c["status"] == "active": - c["status"] = "ended" - with open(campaigns_file, "w") as f: - json.dump(data, f, indent=2) - - if status != "all": - campaign_list = [c for c in campaign_list if c["status"] == status] - - if not campaign_list: - output({"message": "No campaigns found"}, ctx.obj['output_format']) - return - - output(campaign_list, ctx.obj['output_format']) - - -@monitor.command(name="campaign-stats") -@click.argument("campaign_id", required=False) -@click.pass_context -def campaign_stats(ctx, campaign_id: Optional[str]): - """Campaign performance metrics (TVL, participants, rewards)""" - campaigns_file = _ensure_campaigns() - with open(campaigns_file) as f: - data = json.load(f) - - campaign_list = data.get("campaigns", []) - - if campaign_id: - campaign = next((c for c in campaign_list if c["id"] == campaign_id), None) - if not campaign: - error(f"Campaign '{campaign_id}' not found") - ctx.exit(1) - return - targets = [campaign] - else: - targets = campaign_list - - stats = [] - for c in targets: - start = datetime.fromisoformat(c["start_date"]) - end = datetime.fromisoformat(c["end_date"]) - now = datetime.now() - duration_days = (end - start).days - elapsed_days = min((now - start).days, duration_days) - progress_pct = round(elapsed_days / max(duration_days, 1) * 100, 1) - - stats.append({ - "campaign_id": c["id"], - "name": c["name"], - "type": c["type"], - "status": c["status"], - "apy_boost": c.get("apy_boost", 0), - "tvl": c.get("total_staked", 0), - "participants": c.get("participants", 0), - "rewards_distributed": c.get("rewards_distributed", 0), - "duration_days": duration_days, - "elapsed_days": elapsed_days, - "progress_pct": progress_pct, - "start_date": c["start_date"], - "end_date": c["end_date"] - }) - - if len(stats) == 1: - output(stats[0], ctx.obj['output_format']) - else: - output(stats, ctx.obj['output_format']) diff --git a/cli/aitbc_cli/commands/node.py.bak b/cli/aitbc_cli/commands/node.py.bak deleted file mode 100755 index d1f7de99..00000000 --- a/cli/aitbc_cli/commands/node.py.bak +++ /dev/null @@ -1,439 +0,0 @@ -"""Node management commands for AITBC CLI""" - -import click -from typing import Optional -from ..core.config import MultiChainConfig, load_multichain_config, get_default_node_config, add_node_config, remove_node_config -from ..core.node_client import NodeClient -from ..utils import output, error, success - -@click.group() -def node(): - """Node management commands""" - pass - -@node.command() -@click.argument('node_id') -@click.pass_context -def info(ctx, node_id): - """Get detailed node information""" - try: - config = load_multichain_config() - - if node_id not in config.nodes: - error(f"Node {node_id} not found in configuration") - raise click.Abort() - - node_config = config.nodes[node_id] - - import asyncio - - async def get_node_info(): - async with NodeClient(node_config) as client: - return await client.get_node_info() - - node_info = asyncio.run(get_node_info()) - - # Basic node information - basic_info = { - "Node ID": node_info["node_id"], - "Node Type": node_info["type"], - "Status": node_info["status"], - "Version": node_info["version"], - "Uptime": f"{node_info['uptime_days']} days, {node_info['uptime_hours']} hours", - "Endpoint": node_config.endpoint - } - - output(basic_info, ctx.obj.get('output_format', 'table'), title=f"Node Information: {node_id}") - - # Performance metrics - metrics = { - "CPU Usage": f"{node_info['cpu_usage']}%", - "Memory Usage": f"{node_info['memory_usage_mb']:.1f}MB", - "Disk Usage": f"{node_info['disk_usage_mb']:.1f}MB", - "Network In": f"{node_info['network_in_mb']:.1f}MB/s", - "Network Out": f"{node_info['network_out_mb']:.1f}MB/s" - } - - output(metrics, ctx.obj.get('output_format', 'table'), title="Performance Metrics") - - # Hosted chains - if node_info.get("hosted_chains"): - chains_data = [ - { - "Chain ID": chain_id, - "Type": chain.get("type", "unknown"), - "Status": chain.get("status", "unknown") - } - for chain_id, chain in node_info["hosted_chains"].items() - ] - - output(chains_data, ctx.obj.get('output_format', 'table'), title="Hosted Chains") - - except Exception as e: - error(f"Error getting node info: {str(e)}") - raise click.Abort() - -@node.command() -@click.option('--show-private', is_flag=True, help='Show private chains') -@click.option('--node-id', help='Specific node ID to query') -@click.pass_context -def chains(ctx, show_private, node_id): - """List chains hosted on all nodes""" - try: - config = load_multichain_config() - - all_chains = [] - - import asyncio - - async def get_all_chains(): - tasks = [] - for nid, node_config in config.nodes.items(): - if node_id and nid != node_id: - continue - async def get_chains_for_node(nid, nconfig): - try: - async with NodeClient(nconfig) as client: - chains = await client.get_hosted_chains() - return [(nid, chain) for chain in chains] - except Exception as e: - print(f"Error getting chains from node {nid}: {e}") - return [] - - tasks.append(get_chains_for_node(node_id, node_config)) - - results = await asyncio.gather(*tasks) - for result in results: - all_chains.extend(result) - - asyncio.run(get_all_chains()) - - if not all_chains: - output("No chains found on any node", ctx.obj.get('output_format', 'table')) - return - - # Filter private chains if not requested - if not show_private: - all_chains = [(node_id, chain) for node_id, chain in all_chains - if chain.privacy.visibility != "private"] - - # Format output - chains_data = [ - { - "Node ID": node_id, - "Chain ID": chain.id, - "Type": chain.type.value, - "Purpose": chain.purpose, - "Name": chain.name, - "Status": chain.status.value, - "Block Height": chain.block_height, - "Size": f"{chain.size_mb:.1f}MB" - } - for node_id, chain in all_chains - ] - - output(chains_data, ctx.obj.get('output_format', 'table'), title="Chains by Node") - - except Exception as e: - error(f"Error listing chains: {str(e)}") - raise click.Abort() - -@node.command() -@click.option('--format', type=click.Choice(['table', 'json']), default='table', help='Output format') -@click.pass_context -def list(ctx, format): - """List all configured nodes""" - try: - config = load_multichain_config() - - if not config.nodes: - output("No nodes configured", ctx.obj.get('output_format', 'table')) - return - - nodes_data = [ - { - "Node ID": node_id, - "Endpoint": node_config.endpoint, - "Timeout": f"{node_config.timeout}s", - "Max Connections": node_config.max_connections, - "Retry Count": node_config.retry_count - } - for node_id, node_config in config.nodes.items() - ] - - output(nodes_data, ctx.obj.get('output_format', 'table'), title="Configured Nodes") - - except Exception as e: - error(f"Error listing nodes: {str(e)}") - raise click.Abort() - -@node.command() -@click.argument('node_id') -@click.argument('endpoint') -@click.option('--timeout', default=30, help='Request timeout in seconds') -@click.option('--max-connections', default=10, help='Maximum concurrent connections') -@click.option('--retry-count', default=3, help='Number of retry attempts') -@click.pass_context -def add(ctx, node_id, endpoint, timeout, max_connections, retry_count): - """Add a new node to configuration""" - try: - config = load_multichain_config() - - if node_id in config.nodes: - error(f"Node {node_id} already exists") - raise click.Abort() - - node_config = get_default_node_config() - node_config.id = node_id - node_config.endpoint = endpoint - node_config.timeout = timeout - node_config.max_connections = max_connections - node_config.retry_count = retry_count - - config = add_node_config(config, node_config) - - from ..core.config import save_multichain_config - save_multichain_config(config) - - success(f"Node {node_id} added successfully!") - - result = { - "Node ID": node_id, - "Endpoint": endpoint, - "Timeout": f"{timeout}s", - "Max Connections": max_connections, - "Retry Count": retry_count - } - - output(result, ctx.obj.get('output_format', 'table')) - - except Exception as e: - error(f"Error adding node: {str(e)}") - raise click.Abort() - -@node.command() -@click.argument('node_id') -@click.option('--force', is_flag=True, help='Force removal without confirmation') -@click.pass_context -def remove(ctx, node_id, force): - """Remove a node from configuration""" - try: - config = load_multichain_config() - - if node_id not in config.nodes: - error(f"Node {node_id} not found") - raise click.Abort() - - if not force: - # Show node information before removal - node_config = config.nodes[node_id] - node_info = { - "Node ID": node_id, - "Endpoint": node_config.endpoint, - "Timeout": f"{node_config.timeout}s", - "Max Connections": node_config.max_connections - } - - output(node_info, ctx.obj.get('output_format', 'table'), title="Node to Remove") - - if not click.confirm(f"Are you sure you want to remove node {node_id}?"): - raise click.Abort() - - config = remove_node_config(config, node_id) - - from ..core.config import save_multichain_config - save_multichain_config(config) - - success(f"Node {node_id} removed successfully!") - - except Exception as e: - error(f"Error removing node: {str(e)}") - raise click.Abort() - -@node.command() -@click.argument('node_id') -@click.option('--realtime', is_flag=True, help='Real-time monitoring') -@click.option('--interval', default=5, help='Update interval in seconds') -@click.pass_context -def monitor(ctx, node_id, realtime, interval): - """Monitor node activity""" - try: - config = load_multichain_config() - - if node_id not in config.nodes: - error(f"Node {node_id} not found") - raise click.Abort() - - node_config = config.nodes[node_id] - - import asyncio - from rich.console import Console - from rich.layout import Layout - from rich.live import Live - import time - - console = Console() - - async def get_node_stats(): - async with NodeClient(node_config) as client: - node_info = await client.get_node_info() - return node_info - - if realtime: - # Real-time monitoring - def generate_monitor_layout(): - try: - node_info = asyncio.run(get_node_stats()) - - layout = Layout() - layout.split_column( - Layout(name="header", size=3), - Layout(name="metrics"), - Layout(name="chains", size=10) - ) - - # Header - layout["header"].update( - f"Node Monitor: {node_id} - {node_info['status'].upper()}" - ) - - # Metrics table - metrics_data = [ - ["CPU Usage", f"{node_info['cpu_usage']}%"], - ["Memory Usage", f"{node_info['memory_usage_mb']:.1f}MB"], - ["Disk Usage", f"{node_info['disk_usage_mb']:.1f}MB"], - ["Network In", f"{node_info['network_in_mb']:.1f}MB/s"], - ["Network Out", f"{node_info['network_out_mb']:.1f}MB/s"], - ["Uptime", f"{node_info['uptime_days']}d {node_info['uptime_hours']}h"] - ] - - layout["metrics"].update(str(metrics_data)) - - # Chains info - if node_info.get("hosted_chains"): - chains_text = f"Hosted Chains: {len(node_info['hosted_chains'])}\n" - for chain_id, chain in list(node_info["hosted_chains"].items())[:5]: - chains_text += f" • {chain_id} ({chain.get('status', 'unknown')})\n" - layout["chains"].update(chains_text) - else: - layout["chains"].update("No chains hosted") - - return layout - except Exception as e: - return f"Error getting node stats: {e}" - - with Live(generate_monitor_layout(), refresh_per_second=1) as live: - try: - while True: - live.update(generate_monitor_layout()) - time.sleep(interval) - except KeyboardInterrupt: - console.print("\n[yellow]Monitoring stopped by user[/yellow]") - else: - # Single snapshot - node_info = asyncio.run(get_node_stats()) - - stats_data = [ - { - "Metric": "CPU Usage", - "Value": f"{node_info['cpu_usage']}%" - }, - { - "Metric": "Memory Usage", - "Value": f"{node_info['memory_usage_mb']:.1f}MB" - }, - { - "Metric": "Disk Usage", - "Value": f"{node_info['disk_usage_mb']:.1f}MB" - }, - { - "Metric": "Network In", - "Value": f"{node_info['network_in_mb']:.1f}MB/s" - }, - { - "Metric": "Network Out", - "Value": f"{node_info['network_out_mb']:.1f}MB/s" - }, - { - "Metric": "Uptime", - "Value": f"{node_info['uptime_days']}d {node_info['uptime_hours']}h" - } - ] - - output(stats_data, ctx.obj.get('output_format', 'table'), title=f"Node Statistics: {node_id}") - - except Exception as e: - error(f"Error during monitoring: {str(e)}") - raise click.Abort() - -@node.command() -@click.argument('node_id') -@click.pass_context -def test(ctx, node_id): - """Test connectivity to a node""" - try: - config = load_multichain_config() - - if node_id not in config.nodes: - error(f"Node {node_id} not found") - raise click.Abort() - - node_config = config.nodes[node_id] - - import asyncio - - async def test_node(): - try: - async with NodeClient(node_config) as client: - node_info = await client.get_node_info() - chains = await client.get_hosted_chains() - - return { - "connected": True, - "node_id": node_info["node_id"], - "status": node_info["status"], - "version": node_info["version"], - "chains_count": len(chains) - } - except Exception as e: - return { - "connected": False, - "error": str(e) - } - - result = asyncio.run(test_node()) - - if result["connected"]: - success(f"Successfully connected to node {node_id}!") - - test_data = [ - { - "Test": "Connection", - "Status": "✓ Pass" - }, - { - "Test": "Node ID", - "Status": result["node_id"] - }, - { - "Test": "Status", - "Status": result["status"] - }, - { - "Test": "Version", - "Status": result["version"] - }, - { - "Test": "Chains", - "Status": f"{result['chains_count']} hosted" - } - ] - - output(test_data, ctx.obj.get('output_format', 'table'), title=f"Node Test Results: {node_id}") - else: - error(f"Failed to connect to node {node_id}: {result['error']}") - raise click.Abort() - - except Exception as e: - error(f"Error testing node: {str(e)}") - raise click.Abort() diff --git a/dev/scripts/testing/ws_load_test.py.bak b/dev/scripts/testing/ws_load_test.py.bak deleted file mode 100755 index db3d7b1d..00000000 --- a/dev/scripts/testing/ws_load_test.py.bak +++ /dev/null @@ -1,54 +0,0 @@ -from __future__ import annotations - -import asyncio -import json -from contextlib import asynccontextmanager -from typing import List - -import websockets - -DEFAULT_WS_URL = "ws://127.0.0.1:8000/rpc/ws" -BLOCK_TOPIC = "/blocks" -TRANSACTION_TOPIC = "/transactions" - - -async def producer(ws_url: str, interval: float = 0.1, total: int = 100) -> None: - async with websockets.connect(f"{ws_url}{BLOCK_TOPIC}") as websocket: - for index in range(total): - payload = { - "height": index, - "hash": f"0x{index:064x}", - "parent_hash": f"0x{index-1:064x}", - "timestamp": "2025-01-01T00:00:00Z", - "tx_count": 0, - } - await websocket.send(json.dumps(payload)) - await asyncio.sleep(interval) - - -async def consumer(name: str, ws_url: str, path: str, duration: float = 5.0) -> None: - async with websockets.connect(f"{ws_url}{path}") as websocket: - end = asyncio.get_event_loop().time() + duration - received = 0 - while asyncio.get_event_loop().time() < end: - try: - message = await asyncio.wait_for(websocket.recv(), timeout=1.0) - except asyncio.TimeoutError: - continue - received += 1 - if received % 10 == 0: - print(f"[{name}] received {received} messages") - print(f"[{name}] total received: {received}") - - -async def main() -> None: - ws_url = DEFAULT_WS_URL - consumers = [ - consumer("blocks-consumer", ws_url, BLOCK_TOPIC), - consumer("tx-consumer", ws_url, TRANSACTION_TOPIC), - ] - await asyncio.gather(producer(ws_url), *consumers) - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/docs/beginner/01_getting_started/3_cli.md.backup b/docs/beginner/01_getting_started/3_cli.md.backup deleted file mode 100644 index 23c4a422..00000000 --- a/docs/beginner/01_getting_started/3_cli.md.backup +++ /dev/null @@ -1,250 +0,0 @@ -# AITBC CLI Getting Started Guide - -**Complete Command Line Interface Setup and Usage** - -## 🚀 **Quick Start** - -### Prerequisites -- Linux system (Debian 13+ recommended) -- Python 3.13+ installed -- System access (sudo for initial setup) - -### Installation -```bash -# 1. Load development environment -source /opt/aitbc/.env.dev - -# 2. Test CLI installation -aitbc --help -aitbc version - -# 3. Verify services are running -aitbc-services status -``` - -## 🔧 **Development Environment Setup** - -### Permission Configuration -```bash -# Fix permissions (one-time setup) -sudo /opt/aitbc/scripts/clean-sudoers-fix.sh - -# Test permissions -/opt/aitbc/scripts/test-permissions.sh -``` - -### Environment Variables -```bash -# Load development environment -source /opt/aitbc/.env.dev - -# Available aliases -aitbc-services # Service management -aitbc-fix # Quick permission fix -aitbc-logs # View logs -``` - -## 📋 **Basic Operations** - -### Wallet Management -```bash -# Create new wallet -aitbc wallet create --name "my-wallet" - -# List wallets -aitbc wallet list - -# Check balance -aitbc wallet balance --wallet "my-wallet" - -# Get address -aitbc wallet address --wallet "my-wallet" -``` - -### Exchange Operations -```bash -# Register with exchange -aitbc exchange register --name "Binance" --api-key - -# Create trading pair -aitbc exchange create-pair AITBC/BTC - -# Start trading -aitbc exchange start-trading --pair AITBC/BTC - -# Check exchange status -aitbc exchange status -``` - -### Blockchain Operations -```bash -# Get blockchain info -aitbc blockchain info - -# Check node status -aitbc blockchain status - -# List recent blocks -aitbc blockchain blocks --limit 10 - -# Check balance -aitbc blockchain balance --address
-``` - -## 🛠️ **Advanced Usage** - -### Output Formats -```bash -# JSON output -aitbc --output json wallet balance - -# YAML output -aitbc --output yaml blockchain info - -# Table output (default) -aitbc wallet list -``` - -### Debug Mode -```bash -# Enable debug output -aitbc --debug wallet list - -# Test mode (uses mock data) -aitbc --test-mode exchange status - -# Custom timeout -aitbc --timeout 60 blockchain info -``` - -### Configuration -```bash -# Show current configuration -aitbc config show - -# Get specific config value -aitbc config get coordinator_url - -# Set config value -aitbc config set timeout 30 - -# Edit configuration -aitbc config edit -``` - -## 🔍 **Troubleshooting** - -### Common Issues - -#### Permission Denied -```bash -# Fix permissions -/opt/aitbc/scripts/fix-permissions.sh - -# Test permissions -/opt/aitbc/scripts/test-permissions.sh -``` - -#### Service Not Running -```bash -# Check service status -aitbc-services status - -# Restart services -aitbc-services restart - -# View logs -aitbc-logs -``` - -#### Command Not Found -```bash -# Check CLI installation -which aitbc - -# Load environment -source /opt/aitbc/.env.dev - -# Check PATH -echo $PATH | grep aitbc -``` - -#### API Connection Issues -```bash -# Test with debug mode -aitbc --debug blockchain status - -# Test with custom URL -aitbc --url http://localhost:8000 blockchain info - -# Check service endpoints -curl http://localhost:8000/health -``` - -### Debug Mode -```bash -# Enable debug for any command -aitbc --debug - -# Check configuration -aitbc config show - -# Test service connectivity -aitbc --test-mode blockchain status -``` - -## 📚 **Next Steps** - -### Explore Features -1. **Wallet Operations**: Try creating and managing wallets -2. **Exchange Integration**: Register with exchanges and start trading -3. **Blockchain Operations**: Explore blockchain features -4. **Compliance**: Set up KYC/AML verification - -### Advanced Topics -1. **Market Making**: Configure automated trading -2. **Oracle Integration**: Set up price feeds -3. **Security**: Implement multi-sig and time-lock -4. **Development**: Build custom tools and integrations - -### Documentation -- [Complete CLI Reference](../23_cli/README.md) -- [Testing Procedures](../23_cli/testing.md) -- [Permission Setup](../23_cli/permission-setup.md) -- [Exchange Integration](../19_marketplace/exchange_integration.md) - -## 🎯 **Tips and Best Practices** - -### Development Workflow -```bash -# 1. Load environment -source /opt/aitbc/.env.dev - -# 2. Check services -aitbc-services status - -# 3. Test CLI -aitbc version - -# 4. Start development -aitbc wallet create -``` - -### Security Best Practices -- Use strong passwords for wallet encryption -- Enable multi-sig for large amounts -- Keep API keys secure -- Regular backup of wallets -- Monitor compliance requirements - -### Performance Tips -- Use appropriate output formats for automation -- Leverage test mode for development -- Cache frequently used data -- Monitor service health - ---- - -**Last Updated**: March 8, 2026 -**CLI Version**: 0.1.0 -**Test Coverage**: 67/67 tests passing (100%) diff --git a/scripts/dev/ws_load_test.py.bak b/scripts/dev/ws_load_test.py.bak deleted file mode 100644 index db3d7b1d..00000000 --- a/scripts/dev/ws_load_test.py.bak +++ /dev/null @@ -1,54 +0,0 @@ -from __future__ import annotations - -import asyncio -import json -from contextlib import asynccontextmanager -from typing import List - -import websockets - -DEFAULT_WS_URL = "ws://127.0.0.1:8000/rpc/ws" -BLOCK_TOPIC = "/blocks" -TRANSACTION_TOPIC = "/transactions" - - -async def producer(ws_url: str, interval: float = 0.1, total: int = 100) -> None: - async with websockets.connect(f"{ws_url}{BLOCK_TOPIC}") as websocket: - for index in range(total): - payload = { - "height": index, - "hash": f"0x{index:064x}", - "parent_hash": f"0x{index-1:064x}", - "timestamp": "2025-01-01T00:00:00Z", - "tx_count": 0, - } - await websocket.send(json.dumps(payload)) - await asyncio.sleep(interval) - - -async def consumer(name: str, ws_url: str, path: str, duration: float = 5.0) -> None: - async with websockets.connect(f"{ws_url}{path}") as websocket: - end = asyncio.get_event_loop().time() + duration - received = 0 - while asyncio.get_event_loop().time() < end: - try: - message = await asyncio.wait_for(websocket.recv(), timeout=1.0) - except asyncio.TimeoutError: - continue - received += 1 - if received % 10 == 0: - print(f"[{name}] received {received} messages") - print(f"[{name}] total received: {received}") - - -async def main() -> None: - ws_url = DEFAULT_WS_URL - consumers = [ - consumer("blocks-consumer", ws_url, BLOCK_TOPIC), - consumer("tx-consumer", ws_url, TRANSACTION_TOPIC), - ] - await asyncio.gather(producer(ws_url), *consumers) - - -if __name__ == "__main__": - asyncio.run(main())