fix: CLI bugs - network KeyError, mine-status/market-list missing handlers
- Fix network command: use .get() with defaults for chain_id, rpc_version (RPC returns height/hash/timestamp/tx_count, not chain_id/rpc_version) - Add missing dispatch handlers for mine-start, mine-stop, mine-status - Add missing dispatch handlers for market-list, market-create, ai-submit - Enhanced dev_heartbeat.py with AITBC blockchain health checks (monitors local RPC, genesis RPC, height diff, service status)
This commit is contained in:
@@ -1340,11 +1340,11 @@ def main():
|
||||
network_info = get_network_status(rpc_url=args.rpc_url)
|
||||
if network_info:
|
||||
print("Network Status:")
|
||||
print(f" Height: {network_info['height']}")
|
||||
print(f" Latest Block: {network_info['hash'][:16]}...")
|
||||
print(f" Chain ID: {network_info['chain_id']}")
|
||||
print(f" RPC Version: {network_info['rpc_version']}")
|
||||
print(f" Timestamp: {network_info['timestamp']}")
|
||||
print(f" Height: {network_info.get('height', 'N/A')}")
|
||||
print(f" Latest Block: {str(network_info.get('hash', 'N/A'))[:16]}...")
|
||||
print(f" Chain ID: {network_info.get('chain_id', 'ait-mainnet')}")
|
||||
print(f" Tx Count: {network_info.get('tx_count', 0)}")
|
||||
print(f" Timestamp: {network_info.get('timestamp', 'N/A')}")
|
||||
else:
|
||||
sys.exit(1)
|
||||
|
||||
@@ -1507,6 +1507,74 @@ def main():
|
||||
else:
|
||||
sys.exit(1)
|
||||
|
||||
elif args.command == "mine-start":
|
||||
result = mining_operations('start', wallet=args.wallet)
|
||||
if result:
|
||||
print(f"Mining start:")
|
||||
for key, value in result.items():
|
||||
if key != 'action':
|
||||
print(f" {key.replace('_', ' ').title()}: {value}")
|
||||
else:
|
||||
sys.exit(1)
|
||||
|
||||
elif args.command == "mine-stop":
|
||||
result = mining_operations('stop')
|
||||
if result:
|
||||
print(f"Mining stop:")
|
||||
for key, value in result.items():
|
||||
if key != 'action':
|
||||
print(f" {key.replace('_', ' ').title()}: {value}")
|
||||
else:
|
||||
sys.exit(1)
|
||||
|
||||
elif args.command == "mine-status":
|
||||
result = mining_operations('status')
|
||||
if result:
|
||||
print(f"Mining status:")
|
||||
for key, value in result.items():
|
||||
if key != 'action':
|
||||
print(f" {key.replace('_', ' ').title()}: {value}")
|
||||
else:
|
||||
sys.exit(1)
|
||||
|
||||
elif args.command == "market-list":
|
||||
result = marketplace_operations('list', rpc_url=getattr(args, 'rpc_url', DEFAULT_RPC_URL))
|
||||
if result:
|
||||
print(f"Marketplace listings:")
|
||||
for key, value in result.items():
|
||||
if key != 'action':
|
||||
if isinstance(value, list):
|
||||
print(f" {key.replace('_', ' ').title()}:")
|
||||
for item in value:
|
||||
print(f" - {item}")
|
||||
else:
|
||||
print(f" {key.replace('_', ' ').title()}: {value}")
|
||||
else:
|
||||
print("No marketplace listings found.")
|
||||
|
||||
elif args.command == "market-create":
|
||||
result = marketplace_operations('create', name=getattr(args, 'type', ''),
|
||||
price=args.price, description=args.description,
|
||||
wallet=args.wallet)
|
||||
if result:
|
||||
print(f"Marketplace listing created:")
|
||||
for key, value in result.items():
|
||||
if key != 'action':
|
||||
print(f" {key.replace('_', ' ').title()}: {value}")
|
||||
else:
|
||||
sys.exit(1)
|
||||
|
||||
elif args.command == "ai-submit":
|
||||
result = ai_operations('submit', model=getattr(args, 'type', ''),
|
||||
prompt=args.prompt, wallet=args.wallet)
|
||||
if result:
|
||||
print(f"AI job submitted:")
|
||||
for key, value in result.items():
|
||||
if key != 'action':
|
||||
print(f" {key.replace('_', ' ').title()}: {value}")
|
||||
else:
|
||||
sys.exit(1)
|
||||
|
||||
else:
|
||||
parser.print_help()
|
||||
|
||||
|
||||
@@ -13,6 +13,11 @@ from pathlib import Path
|
||||
REPO_ROOT = Path("/opt/aitbc")
|
||||
LOGS_DIR = REPO_ROOT / "logs"
|
||||
|
||||
# AITBC blockchain config
|
||||
LOCAL_RPC = "http://localhost:8006"
|
||||
GENESIS_RPC = "http://10.1.223.93:8006"
|
||||
MAX_HEIGHT_DIFF = 10 # acceptable block height difference between nodes
|
||||
|
||||
def sh(cmd, cwd=REPO_ROOT):
|
||||
"""Run shell command, return (returncode, stdout)."""
|
||||
result = subprocess.run(cmd, shell=True, cwd=cwd, capture_output=True, text=True)
|
||||
@@ -86,7 +91,16 @@ def check_vulnerabilities():
|
||||
"""Run security audits for Python and Node dependencies."""
|
||||
issues = []
|
||||
# Python: pip-audit (if available)
|
||||
rc, out = sh("pip-audit --requirement <(poetry export --without-hashes) 2>&1")
|
||||
# Export requirements to temp file first to avoid shell process substitution issues
|
||||
rc_export, req_content = sh("poetry export --without-hashes")
|
||||
if rc_export == 0 and req_content:
|
||||
import tempfile
|
||||
import os
|
||||
with tempfile.NamedTemporaryFile(mode='w', suffix='.txt', delete=False) as f:
|
||||
f.write(req_content)
|
||||
temp_req_file = f.name
|
||||
try:
|
||||
rc, out = sh(f"pip-audit --requirement {temp_req_file} 2>&1")
|
||||
if rc == 0:
|
||||
# No vulnerabilities
|
||||
pass
|
||||
@@ -98,6 +112,11 @@ def check_vulnerabilities():
|
||||
else:
|
||||
# Command failed for another reason (maybe not installed)
|
||||
pass
|
||||
finally:
|
||||
os.unlink(temp_req_file)
|
||||
else:
|
||||
# Failed to export requirements
|
||||
pass
|
||||
# Node: npm audit (if package.json exists)
|
||||
if (REPO_ROOT / "package.json").exists():
|
||||
rc, out = sh("npm audit --json")
|
||||
@@ -111,10 +130,79 @@ def check_vulnerabilities():
|
||||
issues.append("Node dependencies: npm audit failed to parse")
|
||||
return issues
|
||||
|
||||
def check_blockchain_health():
|
||||
"""Check AITBC blockchain node health on this follower node."""
|
||||
result = {"local_ok": False, "local_height": None, "genesis_ok": False,
|
||||
"genesis_height": None, "sync_diff": None, "services": {},
|
||||
"issues": []}
|
||||
|
||||
# Local RPC health
|
||||
try:
|
||||
import urllib.request
|
||||
with urllib.request.urlopen(f"{LOCAL_RPC}/rpc/head", timeout=5) as resp:
|
||||
data = json.loads(resp.read())
|
||||
result["local_ok"] = True
|
||||
result["local_height"] = data.get("height")
|
||||
except Exception as e:
|
||||
result["issues"].append(f"Local RPC ({LOCAL_RPC}) unreachable: {e}")
|
||||
|
||||
# Genesis node RPC
|
||||
try:
|
||||
import urllib.request
|
||||
with urllib.request.urlopen(f"{GENESIS_RPC}/rpc/head", timeout=5) as resp:
|
||||
data = json.loads(resp.read())
|
||||
result["genesis_ok"] = True
|
||||
result["genesis_height"] = data.get("height")
|
||||
except Exception:
|
||||
result["issues"].append(f"Genesis RPC ({GENESIS_RPC}) unreachable")
|
||||
|
||||
# Sync diff
|
||||
if result["local_height"] is not None and result["genesis_height"] is not None:
|
||||
result["sync_diff"] = result["local_height"] - result["genesis_height"]
|
||||
|
||||
# Service status
|
||||
for svc in ["aitbc-blockchain-node", "aitbc-blockchain-rpc"]:
|
||||
rc, out = sh(f"systemctl is-active {svc}.service")
|
||||
result["services"][svc] = out.strip() if rc == 0 else "unknown"
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def main():
|
||||
report = []
|
||||
issues = 0
|
||||
|
||||
# AITBC Blockchain (always reported)
|
||||
bc = check_blockchain_health()
|
||||
bc_lines = []
|
||||
bc_issue = False
|
||||
if bc["local_ok"]:
|
||||
bc_lines.append(f"- **Follower height**: {bc['local_height']}")
|
||||
else:
|
||||
bc_lines.append("- **Follower RPC**: DOWN")
|
||||
bc_issue = True
|
||||
if bc["genesis_ok"]:
|
||||
bc_lines.append(f"- **Genesis height**: {bc['genesis_height']}")
|
||||
else:
|
||||
bc_lines.append("- **Genesis RPC**: unreachable")
|
||||
bc_issue = True
|
||||
if bc["sync_diff"] is not None:
|
||||
bc_lines.append(f"- **Height diff**: {bc['sync_diff']:+d} (follower {'ahead' if bc['sync_diff'] > 0 else 'behind'})")
|
||||
for svc, status in bc["services"].items():
|
||||
bc_lines.append(f"- **{svc}**: {status}")
|
||||
if status != "active":
|
||||
bc_issue = True
|
||||
for iss in bc["issues"]:
|
||||
bc_lines.append(f"- {iss}")
|
||||
bc_issue = True
|
||||
if bc_issue:
|
||||
issues += 1
|
||||
report.append("### Blockchain: issues detected\n")
|
||||
else:
|
||||
report.append("### Blockchain: healthy\n")
|
||||
report.extend(bc_lines)
|
||||
report.append("")
|
||||
|
||||
# Git
|
||||
git = check_git_status()
|
||||
if git and git["changed"] > 0:
|
||||
|
||||
Reference in New Issue
Block a user