chore: remove configuration files and enhance blockchain explorer with advanced search, analytics, and export features

- Delete .aitbc.yaml.example CLI configuration template
- Delete .lycheeignore link checker exclusion rules
- Delete .nvmrc Node.js version specification
- Add advanced search panel with filters for address, amount range, transaction type, time range, and validator
- Add analytics dashboard with transaction volume, active addresses, and block time metrics
- Add Chart.js integration
This commit is contained in:
oib
2026-03-02 15:38:25 +01:00
parent af185cdd8b
commit ccedbace53
271 changed files with 35942 additions and 2359 deletions

259
cli/examples/miner.py Executable file
View File

@@ -0,0 +1,259 @@
#!/usr/bin/env python3
"""
AITBC Miner CLI Tool - Register, poll for jobs, and submit results
"""
import argparse
import httpx
import json
import sys
import time
from datetime import datetime
from typing import Optional
# Configuration
DEFAULT_COORDINATOR = "http://localhost:8000"
DEFAULT_API_KEY = "${MINER_API_KEY}"
DEFAULT_MINER_ID = "cli-miner"
class AITBCMiner:
def __init__(self, coordinator_url: str, api_key: str, miner_id: str):
self.coordinator_url = coordinator_url
self.api_key = api_key
self.miner_id = miner_id
self.client = httpx.Client()
def register(self, capabilities: dict) -> bool:
"""Register miner with coordinator"""
try:
response = self.client.post(
f"{self.coordinator_url}/v1/miners/register?miner_id={self.miner_id}",
headers={
"Content-Type": "application/json",
"X-Api-Key": self.api_key
},
json={"capabilities": capabilities}
)
if response.status_code == 200:
print(f"✅ Miner {self.miner_id} registered successfully")
return True
else:
print(f"❌ Registration failed: {response.status_code}")
print(f" Response: {response.text}")
return False
except Exception as e:
print(f"❌ Error: {e}")
return False
def poll_job(self, max_wait: int = 5) -> Optional[dict]:
"""Poll for available jobs"""
try:
response = self.client.post(
f"{self.coordinator_url}/v1/miners/poll",
headers={
"Content-Type": "application/json",
"X-Api-Key": self.api_key
},
json={"max_wait_seconds": max_wait}
)
if response.status_code == 200:
return response.json()
elif response.status_code == 204:
return None
else:
print(f"❌ Poll failed: {response.status_code}")
print(f" Response: {response.text}")
return None
except Exception as e:
print(f"❌ Error: {e}")
return None
def submit_result(self, job_id: str, result: dict, metrics: dict = None) -> bool:
"""Submit job result"""
payload = {
"result": result
}
if metrics:
payload["metrics"] = metrics
try:
response = self.client.post(
f"{self.coordinator_url}/v1/miners/{job_id}/result",
headers={
"Content-Type": "application/json",
"X-Api-Key": self.api_key
},
json=payload
)
if response.status_code == 200:
print(f"✅ Result submitted for job {job_id}")
return True
else:
print(f"❌ Submit failed: {response.status_code}")
print(f" Response: {response.text}")
return False
except Exception as e:
print(f"❌ Error: {e}")
return False
def send_heartbeat(self) -> bool:
"""Send heartbeat to coordinator"""
heartbeat_data = {
"status": "ONLINE",
"inflight": 0,
"metadata": {
"last_seen": datetime.utcnow().isoformat(),
"gpu_utilization": 75,
"gpu_memory_used": 8000,
"gpu_temperature": 65
}
}
try:
response = self.client.post(
f"{self.coordinator_url}/v1/miners/heartbeat?miner_id={self.miner_id}",
headers={
"Content-Type": "application/json",
"X-Api-Key": self.api_key
},
json=heartbeat_data
)
return response.status_code == 200
except Exception as e:
print(f"❌ Heartbeat error: {e}")
return False
def mine_continuous(self, max_jobs: int = None, simulate_work: bool = True):
"""Continuously mine jobs"""
print(f"⛏️ Starting continuous mining...")
print(f" Miner ID: {self.miner_id}")
print(f" Max jobs: {max_jobs or 'unlimited'}")
print()
jobs_completed = 0
try:
while max_jobs is None or jobs_completed < max_jobs:
# Poll for job
print("🔍 Polling for jobs...")
job = self.poll_job()
if job:
print(f"✅ Got job: {job['job_id']}")
print(f" Type: {job['payload'].get('type', 'unknown')}")
if simulate_work:
print("⚙️ Processing job...")
time.sleep(2) # Simulate work
# Submit result
result = {
"status": "completed",
"output": f"Job {job['job_id']} processed by {self.miner_id}",
"execution_time_ms": 2000,
"miner_id": self.miner_id
}
metrics = {
"compute_time": 2.0,
"energy_used": 0.1,
"aitbc_earned": 10.0
}
if self.submit_result(job['job_id'], result, metrics):
jobs_completed += 1
print(f"💰 Earned 10 AITBC!")
print(f" Total jobs completed: {jobs_completed}")
# Check if this job is now a block with proposer
print("🔍 Checking block status...")
time.sleep(1)
else:
print("💤 No jobs available, sending heartbeat...")
self.send_heartbeat()
print("-" * 50)
time.sleep(3) # Wait before next poll
except KeyboardInterrupt:
print("\n⏹️ Mining stopped by user")
print(f" Total jobs completed: {jobs_completed}")
def main():
parser = argparse.ArgumentParser(description="AITBC Miner CLI")
parser.add_argument("--url", default=DEFAULT_COORDINATOR, help="Coordinator URL")
parser.add_argument("--api-key", default=DEFAULT_API_KEY, help="API key")
parser.add_argument("--miner-id", default=DEFAULT_MINER_ID, help="Miner ID")
subparsers = parser.add_subparsers(dest="command", help="Commands")
# Register command
register_parser = subparsers.add_parser("register", help="Register miner")
register_parser.add_argument("--gpu", default="RTX 4060 Ti", help="GPU model")
register_parser.add_argument("--memory", type=int, default=16, help="GPU memory GB")
# Poll command
poll_parser = subparsers.add_parser("poll", help="Poll for a job")
poll_parser.add_argument("--wait", type=int, default=5, help="Max wait seconds")
# Mine command
mine_parser = subparsers.add_parser("mine", help="Mine continuously")
mine_parser.add_argument("--jobs", type=int, help="Max jobs to complete")
mine_parser.add_argument("--no-simulate", action="store_true", help="Don't simulate work")
# Heartbeat command
heartbeat_parser = subparsers.add_parser("heartbeat", help="Send heartbeat")
args = parser.parse_args()
if not args.command:
parser.print_help()
return
miner = AITBCMiner(args.url, args.api_key, args.miner_id)
if args.command == "register":
capabilities = {
"gpu": {
"model": args.gpu,
"memory_gb": args.memory,
"cuda_version": "12.4"
},
"compute": {
"type": "GPU",
"platform": "CUDA",
"supported_tasks": ["inference", "training"],
"max_concurrent_jobs": 1
}
}
miner.register(capabilities)
elif args.command == "poll":
print(f"🔍 Polling for jobs (max wait: {args.wait}s)...")
job = miner.poll_job(args.wait)
if job:
print(f"✅ Received job:")
print(json.dumps(job, indent=2))
else:
print("💤 No jobs available")
elif args.command == "mine":
miner.mine_continuous(args.jobs, not args.no_simulate)
elif args.command == "heartbeat":
if miner.send_heartbeat():
print("💓 Heartbeat sent successfully")
else:
print("❌ Heartbeat failed")
if __name__ == "__main__":
main()