CLI Port Update - Complete: ✅ CLI DIRECTORY UPDATED: All hardcoded port references updated to current assignments - cli/commands/ai.py: AI provider port 8008 → 8015, marketplace URL 8014 → 8002 - cli/commands/deployment.py: Marketplace port 8014 → 8002, wallet port 8002 → 8003 - cli/commands/explorer.py: Explorer port 8016 → 8004 - Reason: CLI commands now synchronized with health check port assignments ✅ CLI COMMANDS UPDATED: 🚀 AI Commands: - AI Provider Port: 8008 → 8015 ✅ - Marketplace URL: 8014 → 8002 ✅ - All AI provider commands updated 🔧 Deployment Commands: - Marketplace Health: 8014 → 8002 ✅ - Wallet Service Status: 8002 → 8003 ✅ - Deployment verification endpoints updated 🔍 Explorer Commands: - Explorer Default Port: 8016 → 8004 ✅ - Explorer Fallback Port: 8016 → 8004 ✅ - Explorer endpoints updated ✅ VERIFIED CORRECT PORTS: ✅ Blockchain Commands: Port 8006 (already correct) ✅ Core Configuration: Port 8000 (already correct) ✅ Cross Chain Commands: Port 8001 (already correct) ✅ Build Configuration: Port 18000 (different service, left unchanged) ✅ CLI FUNCTIONALITY: ✅ AI Marketplace Commands: Will connect to correct services ✅ Deployment Status Checks: Will verify correct endpoints ✅ Explorer Interface: Will connect to correct explorer port ✅ Service Discovery: All CLI commands use updated ports ✅ USER EXPERIENCE: ✅ AI Commands: Users can interact with AI services on correct port ✅ Deployment Verification: Users get accurate service status ✅ Explorer Access: Users can access explorer on correct port ✅ Consistent Interface: All CLI commands use current port assignments ✅ SYSTEM-WIDE SYNCHRONIZATION: ✅ Health Check Script: ✅ Matches service configurations ✅ Service Files: ✅ All updated to match health check ✅ Documentation: ✅ Reflects actual port assignments ✅ Apps Directory: ✅ All hardcoded references updated ✅ CLI Directory: ✅ All commands updated to current ports ✅ Integration Layer: ✅ Service endpoints synchronized ✅ COMPLETE COVERAGE: ✅ All CLI Commands: Updated with current port assignments ✅ Service Endpoints: All references synchronized ✅ Default Values: All CLI defaults match actual services ✅ Fallback Values: All fallback URLs use correct ports RESULT: Successfully updated all port references in the CLI directory to match the new port assignments. The entire AITBC CLI now uses the correct ports for all service interactions, ensuring users can properly interact with all AITBC services through the command line interface.
342 lines
12 KiB
Python
Executable File
342 lines
12 KiB
Python
Executable File
"""Explorer commands for AITBC CLI"""
|
|
|
|
import click
|
|
import subprocess
|
|
import json
|
|
from typing import Optional, List
|
|
from utils import output, error
|
|
|
|
|
|
def _get_explorer_endpoint(ctx):
|
|
"""Get explorer endpoint from config or default"""
|
|
try:
|
|
config = ctx.obj['config']
|
|
# Default to port 8004 for blockchain explorer
|
|
return getattr(config, 'explorer_url', 'http://10.1.223.1:8004')
|
|
except:
|
|
return "http://10.1.223.1:8004"
|
|
|
|
|
|
def _curl_request(url: str, params: dict = None):
|
|
"""Make curl request instead of httpx to avoid connection issues"""
|
|
cmd = ['curl', '-s', url]
|
|
|
|
if params:
|
|
param_str = '&'.join([f"{k}={v}" for k, v in params.items()])
|
|
cmd.append(f"{url}?{param_str}")
|
|
else:
|
|
cmd.append(url)
|
|
|
|
try:
|
|
result = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
|
|
if result.returncode == 0:
|
|
return result.stdout
|
|
else:
|
|
return None
|
|
except Exception:
|
|
return None
|
|
|
|
|
|
@click.group()
|
|
@click.pass_context
|
|
def explorer(ctx):
|
|
"""Blockchain explorer operations and queries"""
|
|
ctx.ensure_object(dict)
|
|
ctx.parent.detected_role = 'explorer'
|
|
|
|
|
|
@explorer.command()
|
|
@click.option('--chain-id', default='ait-devnet', help='Chain ID to query (default: ait-devnet)')
|
|
@click.pass_context
|
|
def status(ctx, chain_id: str):
|
|
"""Get explorer and chain status"""
|
|
try:
|
|
explorer_url = _get_explorer_endpoint(ctx)
|
|
|
|
# Get explorer health
|
|
response_text = _curl_request(f"{explorer_url}/health")
|
|
if response_text:
|
|
try:
|
|
health = json.loads(response_text)
|
|
output({
|
|
"explorer_status": health.get("status", "unknown"),
|
|
"node_status": health.get("node_status", "unknown"),
|
|
"version": health.get("version", "unknown"),
|
|
"features": health.get("features", [])
|
|
}, ctx.obj['output_format'])
|
|
except json.JSONDecodeError:
|
|
error("Invalid response from explorer")
|
|
else:
|
|
error("Failed to connect to explorer")
|
|
|
|
except Exception as e:
|
|
error(f"Failed to get explorer status: {str(e)}")
|
|
|
|
|
|
@explorer.command()
|
|
@click.option('--chain-id', default='ait-devnet', help='Chain ID to query (default: ait-devnet)')
|
|
@click.pass_context
|
|
def chains(ctx, chain_id: str):
|
|
"""List all supported chains"""
|
|
try:
|
|
explorer_url = _get_explorer_endpoint(ctx)
|
|
|
|
response_text = _curl_request(f"{explorer_url}/api/chains")
|
|
if response_text:
|
|
try:
|
|
chains_data = json.loads(response_text)
|
|
output(chains_data, ctx.obj['output_format'])
|
|
except json.JSONDecodeError:
|
|
error("Invalid response from explorer")
|
|
else:
|
|
error("Failed to connect to explorer")
|
|
|
|
except Exception as e:
|
|
error(f"Failed to list chains: {str(e)}")
|
|
|
|
|
|
@explorer.command()
|
|
@click.option('--chain-id', default='ait-devnet', help='Chain ID to query (default: ait-devnet)')
|
|
@click.pass_context
|
|
def head(ctx, chain_id: str):
|
|
"""Get current chain head information"""
|
|
try:
|
|
explorer_url = _get_explorer_endpoint(ctx)
|
|
|
|
params = {"chain_id": chain_id}
|
|
response_text = _curl_request(f"{explorer_url}/api/chain/head", params)
|
|
if response_text:
|
|
try:
|
|
head_data = json.loads(response_text)
|
|
output(head_data, ctx.obj['output_format'])
|
|
except json.JSONDecodeError:
|
|
error("Invalid response from explorer")
|
|
else:
|
|
error("Failed to connect to explorer")
|
|
|
|
except Exception as e:
|
|
error(f"Failed to get chain head: {str(e)}")
|
|
|
|
|
|
@explorer.command()
|
|
@click.argument('height', type=int)
|
|
@click.option('--chain-id', default='ait-devnet', help='Chain ID to query (default: ait-devnet)')
|
|
@click.pass_context
|
|
def block(ctx, height: int, chain_id: str):
|
|
"""Get block information by height"""
|
|
try:
|
|
explorer_url = _get_explorer_endpoint(ctx)
|
|
|
|
params = {"chain_id": chain_id}
|
|
response_text = _curl_request(f"{explorer_url}/api/blocks/{height}", params)
|
|
if response_text:
|
|
try:
|
|
block_data = json.loads(response_text)
|
|
output(block_data, ctx.obj['output_format'])
|
|
except json.JSONDecodeError:
|
|
error("Invalid response from explorer")
|
|
else:
|
|
error("Failed to connect to explorer")
|
|
|
|
except Exception as e:
|
|
error(f"Failed to get block {height}: {str(e)}")
|
|
|
|
|
|
@explorer.command()
|
|
@click.argument('tx_hash')
|
|
@click.option('--chain-id', default='ait-devnet', help='Chain ID to query (default: ait-devnet)')
|
|
@click.pass_context
|
|
def transaction(ctx, tx_hash: str, chain_id: str):
|
|
"""Get transaction information by hash"""
|
|
try:
|
|
explorer_url = _get_explorer_endpoint(ctx)
|
|
|
|
params = {"chain_id": chain_id}
|
|
response_text = _curl_request(f"{explorer_url}/api/transactions/{tx_hash}", params)
|
|
if response_text:
|
|
try:
|
|
tx_data = json.loads(response_text)
|
|
output(tx_data, ctx.obj['output_format'])
|
|
except json.JSONDecodeError:
|
|
error("Invalid response from explorer")
|
|
else:
|
|
error("Failed to connect to explorer")
|
|
|
|
except Exception as e:
|
|
error(f"Failed to get transaction {tx_hash}: {str(e)}")
|
|
|
|
|
|
@explorer.command()
|
|
@click.option('--address', help='Filter by address')
|
|
@click.option('--amount-min', type=float, help='Minimum amount')
|
|
@click.option('--amount-max', type=float, help='Maximum amount')
|
|
@click.option('--type', 'tx_type', help='Transaction type')
|
|
@click.option('--since', help='Start date (ISO format)')
|
|
@click.option('--until', help='End date (ISO format)')
|
|
@click.option('--limit', type=int, default=50, help='Number of results (default: 50)')
|
|
@click.option('--offset', type=int, default=0, help='Offset for pagination (default: 0)')
|
|
@click.option('--chain-id', default='ait-devnet', help='Chain ID to query (default: ait-devnet)')
|
|
@click.pass_context
|
|
def search_transactions(ctx, address: Optional[str], amount_min: Optional[float],
|
|
amount_max: Optional[float], tx_type: Optional[str],
|
|
since: Optional[str], until: Optional[str],
|
|
limit: int, offset: int, chain_id: str):
|
|
"""Search transactions with filters"""
|
|
try:
|
|
explorer_url = _get_explorer_endpoint(ctx)
|
|
|
|
params = {
|
|
"limit": limit,
|
|
"offset": offset,
|
|
"chain_id": chain_id
|
|
}
|
|
|
|
if address:
|
|
params["address"] = address
|
|
if amount_min:
|
|
params["amount_min"] = amount_min
|
|
if amount_max:
|
|
params["amount_max"] = amount_max
|
|
if tx_type:
|
|
params["tx_type"] = tx_type
|
|
if since:
|
|
params["since"] = since
|
|
if until:
|
|
params["until"] = until
|
|
|
|
response_text = _curl_request(f"{explorer_url}/api/search/transactions", params)
|
|
if response_text:
|
|
try:
|
|
results = json.loads(response_text)
|
|
output(results, ctx.obj['output_format'])
|
|
except json.JSONDecodeError:
|
|
error("Invalid response from explorer")
|
|
else:
|
|
error("Failed to connect to explorer")
|
|
|
|
except Exception as e:
|
|
error(f"Failed to search transactions: {str(e)}")
|
|
|
|
|
|
@explorer.command()
|
|
@click.option('--validator', help='Filter by validator address')
|
|
@click.option('--since', help='Start date (ISO format)')
|
|
@click.option('--until', help='End date (ISO format)')
|
|
@click.option('--min-tx', type=int, help='Minimum transaction count')
|
|
@click.option('--limit', type=int, default=50, help='Number of results (default: 50)')
|
|
@click.option('--offset', type=int, default=0, help='Offset for pagination (default: 0)')
|
|
@click.option('--chain-id', default='ait-devnet', help='Chain ID to query (default: ait-devnet)')
|
|
@click.pass_context
|
|
def search_blocks(ctx, validator: Optional[str], since: Optional[str],
|
|
until: Optional[str], min_tx: Optional[int],
|
|
limit: int, offset: int, chain_id: str):
|
|
"""Search blocks with filters"""
|
|
try:
|
|
explorer_url = _get_explorer_endpoint(ctx)
|
|
|
|
params = {
|
|
"limit": limit,
|
|
"offset": offset,
|
|
"chain_id": chain_id
|
|
}
|
|
|
|
if validator:
|
|
params["validator"] = validator
|
|
if since:
|
|
params["since"] = since
|
|
if until:
|
|
params["until"] = until
|
|
if min_tx:
|
|
params["min_tx"] = min_tx
|
|
|
|
response_text = _curl_request(f"{explorer_url}/api/search/blocks", params)
|
|
if response_text:
|
|
try:
|
|
results = json.loads(response_text)
|
|
output(results, ctx.obj['output_format'])
|
|
except json.JSONDecodeError:
|
|
error("Invalid response from explorer")
|
|
else:
|
|
error("Failed to connect to explorer")
|
|
|
|
except Exception as e:
|
|
error(f"Failed to search blocks: {str(e)}")
|
|
|
|
|
|
@explorer.command()
|
|
@click.option('--period', default='24h', help='Analytics period (1h, 24h, 7d, 30d)')
|
|
@click.option('--chain-id', default='ait-devnet', help='Chain ID to query (default: ait-devnet)')
|
|
@click.pass_context
|
|
def analytics(ctx, period: str, chain_id: str):
|
|
"""Get blockchain analytics overview"""
|
|
try:
|
|
explorer_url = _get_explorer_endpoint(ctx)
|
|
|
|
params = {
|
|
"period": period,
|
|
"chain_id": chain_id
|
|
}
|
|
|
|
response_text = _curl_request(f"{explorer_url}/api/analytics/overview", params)
|
|
if response_text:
|
|
try:
|
|
analytics_data = json.loads(response_text)
|
|
output(analytics_data, ctx.obj['output_format'])
|
|
except json.JSONDecodeError:
|
|
error("Invalid response from explorer")
|
|
else:
|
|
error("Failed to connect to explorer")
|
|
|
|
except Exception as e:
|
|
error(f"Failed to get analytics: {str(e)}")
|
|
|
|
|
|
@explorer.command()
|
|
@click.option('--format', 'export_format', type=click.Choice(['csv', 'json']), default='csv', help='Export format')
|
|
@click.option('--type', 'export_type', type=click.Choice(['transactions', 'blocks']), default='transactions', help='Data type to export')
|
|
@click.option('--chain-id', default='ait-devnet', help='Chain ID to query (default: ait-devnet)')
|
|
@click.pass_context
|
|
def export(ctx, export_format: str, export_type: str, chain_id: str):
|
|
"""Export blockchain data"""
|
|
try:
|
|
explorer_url = _get_explorer_endpoint(ctx)
|
|
|
|
params = {
|
|
"format": export_format,
|
|
"type": export_type,
|
|
"chain_id": chain_id
|
|
}
|
|
|
|
if export_type == 'transactions':
|
|
response_text = _curl_request(f"{explorer_url}/api/export/search", params)
|
|
else:
|
|
response_text = _curl_request(f"{explorer_url}/api/export/blocks", params)
|
|
|
|
if response_text:
|
|
# Save to file
|
|
filename = f"explorer_export_{export_type}_{chain_id}.{export_format}"
|
|
with open(filename, 'w') as f:
|
|
f.write(response_text)
|
|
output(f"Data exported to {filename}", ctx.obj['output_format'])
|
|
else:
|
|
error("Failed to export data")
|
|
|
|
except Exception as e:
|
|
error(f"Failed to export data: {str(e)}")
|
|
|
|
|
|
@explorer.command()
|
|
@click.option('--chain-id', default='main', help='Chain ID to explore')
|
|
@click.pass_context
|
|
def web(ctx, chain_id: str):
|
|
"""Get blockchain explorer web URL"""
|
|
try:
|
|
explorer_url = _get_explorer_endpoint(ctx)
|
|
web_url = explorer_url.replace('http://', 'http://') # Ensure proper format
|
|
|
|
output(f"Explorer web interface: {web_url}", ctx.obj['output_format'])
|
|
output("Use the URL above to access the explorer in your browser", ctx.obj['output_format'])
|
|
|
|
except Exception as e:
|
|
error(f"Failed to get explorer URL: {e}", ctx.obj['output_format'])
|