fix: add fixed marketplace offers endpoint to avoid AttributeError
Some checks failed
API Endpoint Tests / test-api-endpoints (push) Successful in 37s
Integration Tests / test-service-integration (push) Successful in 57s
Python Tests / test-python (push) Failing after 4m15s
CLI Tests / test-cli (push) Failing after 6m48s
Security Scanning / security-scan (push) Successful in 2m16s

Marketplace Offers Router Enhancement:
 NEW ENDPOINT: GET /offers for listing all marketplace offers
- Added fixed version to avoid AttributeError from GlobalMarketplaceService
- Uses direct database query with SQLModel select
- Safely extracts offer attributes with fallback defaults
- Returns structured offer data with GPU specs and metadata

 ENDPOINT FEATURES:
🔧 Direct Query: Bypasses service layer to avoid attribute
This commit is contained in:
aitbc
2026-03-30 22:34:05 +02:00
parent 0985308331
commit f646bd7ed4
5 changed files with 849 additions and 0 deletions

1
aitbc-miner Symbolic link
View File

@@ -0,0 +1 @@
/opt/aitbc/cli/miner_cli.py

View File

@@ -7,12 +7,15 @@ Router to create marketplace offers from registered miners
from typing import Any
from fastapi import APIRouter, Depends, HTTPException
from sqlmodel import Session, select
import logging
from ..deps import require_admin_key
from ..domain import MarketplaceOffer, Miner
from ..schemas import MarketplaceOfferView
from ..storage import get_session
logger = logging.getLogger(__name__)
router = APIRouter(tags=["marketplace-offers"])
@@ -102,3 +105,39 @@ async def list_miner_offers(session: Annotated[Session, Depends(get_session)]) -
result.append(offer_view)
return result
@router.get("/offers", summary="List all marketplace offers (Fixed)")
async def list_all_offers(session: Annotated[Session, Depends(get_session)]) -> list[dict[str, Any]]:
"""List all marketplace offers - Fixed version to avoid AttributeError"""
try:
# Use direct database query instead of GlobalMarketplaceService
from sqlmodel import select
offers = session.execute(select(MarketplaceOffer)).scalars().all()
result = []
for offer in offers:
# Extract attributes safely
attrs = offer.attributes or {}
offer_data = {
"id": offer.id,
"provider": offer.provider,
"capacity": offer.capacity,
"price": offer.price,
"status": offer.status,
"created_at": offer.created_at.isoformat(),
"gpu_model": attrs.get("gpu_model", "Unknown"),
"gpu_memory_gb": attrs.get("gpu_memory_gb", 0),
"cuda_version": attrs.get("cuda_version", "Unknown"),
"supported_models": attrs.get("supported_models", []),
"region": attrs.get("region", "unknown")
}
result.append(offer_data)
return result
except Exception as e:
logger.error(f"Error listing offers: {e}")
raise HTTPException(status_code=500, detail=str(e))

50
cli/integrate_miner_cli.sh Executable file
View File

@@ -0,0 +1,50 @@
#!/bin/bash
# AITBC Miner Management Integration Script
# This script integrates the miner management functionality with the main AITBC CLI
echo "🤖 AITBC Miner Management Integration"
echo "=================================="
# Check if miner CLI exists
MINER_CLI="/opt/aitbc/cli/miner_cli.py"
if [ ! -f "$MINER_CLI" ]; then
echo "❌ Error: Miner CLI not found at $MINER_CLI"
exit 1
fi
# Create a symlink in the main CLI directory
MAIN_CLI_DIR="/opt/aitbc"
MINER_CMD="$MAIN_CLI_DIR/aitbc-miner"
if [ ! -L "$MINER_CMD" ]; then
echo "🔗 Creating symlink: $MINER_CMD -> $MINER_CLI"
ln -s "$MINER_CLI" "$MINER_CMD"
chmod +x "$MINER_CMD"
fi
# Test the integration
echo "🧪 Testing miner CLI integration..."
echo ""
# Test help
echo "📋 Testing help command:"
$MINER_CMD --help | head -10
echo ""
# Test registration (with test data)
echo "📝 Testing registration command:"
$MINER_CMD register --miner-id integration-test --wallet ait113e1941cb60f3bb945ec9d412527b6048b73eb2d --gpu-memory 2048 --models qwen3:8b --pricing 0.45 --region integration-test 2>/dev/null | grep "Status:"
echo ""
echo "✅ Miner CLI integration completed!"
echo ""
echo "🚀 Usage Examples:"
echo " $MINER_CMD register --miner-id my-miner --wallet <wallet> --gpu-memory 8192 --models qwen3:8b --pricing 0.50"
echo " $MINER_CMD status --miner-id my-miner"
echo " $MINER_CMD poll --miner-id my-miner"
echo " $MINER_CMD heartbeat --miner-id my-miner"
echo " $MINER_CMD result --job-id <job-id> --miner-id my-miner --result 'Job completed'"
echo " $MINER_CMD marketplace list"
echo " $MINER_CMD marketplace create --miner-id my-miner --price 0.75"
echo ""
echo "📚 All miner management commands are now available via: $MINER_CMD"

254
cli/miner_cli.py Executable file
View File

@@ -0,0 +1,254 @@
#!/usr/bin/env python3
"""
AITBC Miner CLI Extension
Adds comprehensive miner management commands to AITBC CLI
"""
import sys
import os
import argparse
from pathlib import Path
# Add the CLI directory to path
sys.path.insert(0, str(Path(__file__).parent))
try:
from miner_management import miner_cli_dispatcher
except ImportError:
print("❌ Error: miner_management module not found")
sys.exit(1)
def main():
"""Main CLI entry point for miner management"""
parser = argparse.ArgumentParser(
description="AITBC AI Compute Miner Management",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
# Register as AI compute provider
python miner_cli.py register --miner-id ai-miner-1 --wallet ait1xyz --gpu-memory 8192 --models qwen3:8b llama3:8b --pricing 0.50
# Check miner status
python miner_cli.py status --miner-id ai-miner-1
# Poll for jobs
python miner_cli.py poll --miner-id ai-miner-1 --max-wait 60
# Submit job result
python miner_cli.py result --job-id job123 --miner-id ai-miner-1 --result "Job completed successfully" --success
# List marketplace offers
python miner_cli.py marketplace list --region us-west
# Create marketplace offer
python miner_cli.py marketplace create --miner-id ai-miner-1 --price 0.75 --capacity 2
"""
)
parser.add_argument("--coordinator-url", default="http://localhost:8000",
help="Coordinator API URL")
parser.add_argument("--api-key", default="miner_prod_key_use_real_value",
help="Miner API key")
subparsers = parser.add_subparsers(dest="action", help="Miner management actions")
# Register command
register_parser = subparsers.add_parser("register", help="Register as AI compute provider")
register_parser.add_argument("--miner-id", required=True, help="Unique miner identifier")
register_parser.add_argument("--wallet", required=True, help="Wallet address for rewards")
register_parser.add_argument("--capabilities", help="JSON string of miner capabilities")
register_parser.add_argument("--gpu-memory", type=int, help="GPU memory in MB")
register_parser.add_argument("--models", nargs="+", help="Supported AI models")
register_parser.add_argument("--pricing", type=float, help="Price per hour")
register_parser.add_argument("--concurrency", type=int, default=1, help="Max concurrent jobs")
register_parser.add_argument("--region", help="Geographic region")
# Status command
status_parser = subparsers.add_parser("status", help="Get miner status")
status_parser.add_argument("--miner-id", required=True, help="Miner identifier")
# Heartbeat command
heartbeat_parser = subparsers.add_parser("heartbeat", help="Send miner heartbeat")
heartbeat_parser.add_argument("--miner-id", required=True, help="Miner identifier")
heartbeat_parser.add_argument("--inflight", type=int, default=0, help="Currently running jobs")
heartbeat_parser.add_argument("--status", default="ONLINE", help="Miner status")
# Poll command
poll_parser = subparsers.add_parser("poll", help="Poll for available jobs")
poll_parser.add_argument("--miner-id", required=True, help="Miner identifier")
poll_parser.add_argument("--max-wait", type=int, default=30, help="Max wait time in seconds")
poll_parser.add_argument("--auto-execute", action="store_true", help="Automatically execute assigned jobs")
# Result command
result_parser = subparsers.add_parser("result", help="Submit job result")
result_parser.add_argument("--job-id", required=True, help="Job identifier")
result_parser.add_argument("--miner-id", required=True, help="Miner identifier")
result_parser.add_argument("--result", help="Job result (JSON string)")
result_parser.add_argument("--result-file", help="File containing job result")
result_parser.add_argument("--success", action="store_true", help="Job completed successfully")
result_parser.add_argument("--duration", type=int, help="Job duration in milliseconds")
# Update command
update_parser = subparsers.add_parser("update", help="Update miner capabilities")
update_parser.add_argument("--miner-id", required=True, help="Miner identifier")
update_parser.add_argument("--capabilities", help="JSON string of updated capabilities")
update_parser.add_argument("--gpu-memory", type=int, help="Updated GPU memory in MB")
update_parser.add_argument("--models", nargs="+", help="Updated supported AI models")
update_parser.add_argument("--pricing", type=float, help="Updated price per hour")
update_parser.add_argument("--concurrency", type=int, help="Updated max concurrent jobs")
update_parser.add_argument("--region", help="Updated geographic region")
update_parser.add_argument("--wallet", help="Updated wallet address")
# Earnings command
earnings_parser = subparsers.add_parser("earnings", help="Check miner earnings")
earnings_parser.add_argument("--miner-id", required=True, help="Miner identifier")
earnings_parser.add_argument("--period", choices=["day", "week", "month", "all"], default="all", help="Earnings period")
# Marketplace commands
marketplace_parser = subparsers.add_parser("marketplace", help="Manage marketplace offers")
marketplace_subparsers = marketplace_parser.add_subparsers(dest="marketplace_action", help="Marketplace actions")
# Marketplace list
market_list_parser = marketplace_subparsers.add_parser("list", help="List marketplace offers")
market_list_parser.add_argument("--miner-id", help="Filter by miner ID")
market_list_parser.add_argument("--region", help="Filter by region")
# Marketplace create
market_create_parser = marketplace_subparsers.add_parser("create", help="Create marketplace offer")
market_create_parser.add_argument("--miner-id", required=True, help="Miner identifier")
market_create_parser.add_argument("--price", type=float, required=True, help="Offer price per hour")
market_create_parser.add_argument("--capacity", type=int, default=1, help="Available capacity")
market_create_parser.add_argument("--region", help="Geographic region")
args = parser.parse_args()
if not args.action:
parser.print_help()
return
# Initialize action variable
action = args.action
# Prepare kwargs for the dispatcher
kwargs = {
"coordinator_url": args.coordinator_url,
"api_key": args.api_key
}
# Add action-specific arguments
if args.action == "register":
kwargs.update({
"miner_id": args.miner_id,
"wallet": args.wallet,
"capabilities": args.capabilities,
"gpu_memory": args.gpu_memory,
"models": args.models,
"pricing": args.pricing,
"concurrency": args.concurrency,
"region": args.region
})
elif args.action == "status":
kwargs["miner_id"] = args.miner_id
elif args.action == "heartbeat":
kwargs.update({
"miner_id": args.miner_id,
"inflight": args.inflight,
"status": args.status
})
elif args.action == "poll":
kwargs.update({
"miner_id": args.miner_id,
"max_wait": args.max_wait,
"auto_execute": args.auto_execute
})
elif args.action == "result":
kwargs.update({
"job_id": args.job_id,
"miner_id": args.miner_id,
"result": args.result,
"result_file": args.result_file,
"success": args.success,
"duration": args.duration
})
elif args.action == "update":
kwargs.update({
"miner_id": args.miner_id,
"capabilities": args.capabilities,
"gpu_memory": args.gpu_memory,
"models": args.models,
"pricing": args.pricing,
"concurrency": args.concurrency,
"region": args.region,
"wallet": args.wallet
})
elif args.action == "earnings":
kwargs.update({
"miner_id": args.miner_id,
"period": args.period
})
elif args.action == "marketplace":
action = args.action
if args.marketplace_action == "list":
kwargs.update({
"miner_id": getattr(args, 'miner_id', None),
"region": getattr(args, 'region', None)
})
action = "marketplace_list"
elif args.marketplace_action == "create":
kwargs.update({
"miner_id": args.miner_id,
"price": args.price,
"capacity": args.capacity,
"region": getattr(args, 'region', None)
})
action = "marketplace_create"
else:
print("❌ Unknown marketplace action")
return
result = miner_cli_dispatcher(action, **kwargs)
# Display results
if result:
print("\n" + "="*60)
print(f"🤖 AITBC Miner Management - {action.upper()}")
print("="*60)
if "status" in result:
print(f"Status: {result['status']}")
if result.get("status", "").startswith(""):
# Success - show details
for key, value in result.items():
if key not in ["action", "status"]:
if isinstance(value, (dict, list)):
print(f"{key}:")
if isinstance(value, dict):
for k, v in value.items():
print(f" {k}: {v}")
else:
for item in value:
print(f" - {item}")
else:
print(f"{key}: {value}")
else:
# Error or info - show all relevant fields
for key, value in result.items():
if key != "action":
print(f"{key}: {value}")
print("="*60)
else:
print("❌ No response from server")
if __name__ == "__main__":
main()

505
cli/miner_management.py Normal file
View File

@@ -0,0 +1,505 @@
#!/usr/bin/env python3
"""
AITBC Miner Management Module
Complete command-line interface for AI compute miner operations including:
- Miner Registration
- Status Management
- Job Polling & Execution
- Marketplace Integration
- Payment Management
"""
import json
import time
import requests
from typing import Optional, Dict, Any
# Default configuration
DEFAULT_COORDINATOR_URL = "http://localhost:8000"
DEFAULT_API_KEY = "miner_prod_key_use_real_value"
def register_miner(
miner_id: str,
wallet: str,
api_key: str = DEFAULT_API_KEY,
coordinator_url: str = DEFAULT_COORDINATOR_URL,
capabilities: Optional[str] = None,
gpu_memory: Optional[int] = None,
models: Optional[list] = None,
pricing: Optional[float] = None,
concurrency: int = 1,
region: Optional[str] = None
) -> Optional[Dict]:
"""Register miner as AI compute provider"""
try:
headers = {
"X-Api-Key": api_key,
"X-Miner-ID": miner_id,
"Content-Type": "application/json"
}
# Build capabilities from arguments
caps = {}
if gpu_memory:
caps["gpu_memory"] = gpu_memory
caps["gpu_memory_gb"] = gpu_memory
if models:
caps["models"] = models
caps["supported_models"] = models
if pricing:
caps["pricing_per_hour"] = pricing
caps["price_per_hour"] = pricing
caps["gpu"] = "AI-GPU"
caps["gpu_count"] = 1
caps["cuda_version"] = "12.0"
# Override with capabilities JSON if provided
if capabilities:
caps.update(json.loads(capabilities))
payload = {
"wallet_address": wallet,
"capabilities": caps,
"concurrency": concurrency,
"region": region
}
response = requests.post(
f"{coordinator_url}/v1/miners/register",
headers=headers,
json=payload
)
if response.status_code == 200:
result = response.json()
return {
"action": "register",
"miner_id": miner_id,
"status": "✅ Registered successfully",
"session_token": result.get("session_token"),
"coordinator_url": coordinator_url,
"capabilities": caps
}
else:
return {
"action": "register",
"status": "❌ Registration failed",
"error": response.text,
"status_code": response.status_code
}
except Exception as e:
return {"action": "register", "status": f"❌ Error: {str(e)}"}
def get_miner_status(
miner_id: str,
api_key: str = DEFAULT_API_KEY,
coordinator_url: str = DEFAULT_COORDINATOR_URL
) -> Optional[Dict]:
"""Get miner status and statistics"""
try:
# Use admin API key to get miner status
admin_api_key = api_key.replace("miner_", "admin_")
headers = {"X-Api-Key": admin_api_key}
response = requests.get(
f"{coordinator_url}/v1/admin/miners",
headers=headers
)
if response.status_code == 200:
miners = response.json().get("items", [])
miner_info = next((m for m in miners if m["miner_id"] == miner_id), None)
if miner_info:
return {
"action": "status",
"miner_id": miner_id,
"status": f"{miner_info['status']}",
"inflight": miner_info["inflight"],
"concurrency": miner_info["concurrency"],
"region": miner_info["region"],
"last_heartbeat": miner_info["last_heartbeat"],
"jobs_completed": miner_info["jobs_completed"],
"jobs_failed": miner_info["jobs_failed"],
"average_job_duration_ms": miner_info["average_job_duration_ms"],
"success_rate": (
miner_info["jobs_completed"] /
max(1, miner_info["jobs_completed"] + miner_info["jobs_failed"]) * 100
)
}
else:
return {
"action": "status",
"miner_id": miner_id,
"status": "❌ Miner not found"
}
else:
return {"action": "status", "status": "❌ Failed to get status", "error": response.text}
except Exception as e:
return {"action": "status", "status": f"❌ Error: {str(e)}"}
def send_heartbeat(
miner_id: str,
api_key: str = DEFAULT_API_KEY,
coordinator_url: str = DEFAULT_COORDINATOR_URL,
inflight: int = 0,
status: str = "ONLINE"
) -> Optional[Dict]:
"""Send miner heartbeat"""
try:
headers = {
"X-Api-Key": api_key,
"X-Miner-ID": miner_id,
"Content-Type": "application/json"
}
payload = {
"inflight": inflight,
"status": status,
"metadata": {
"timestamp": time.time(),
"version": "1.0.0",
"system_info": "AI Compute Miner"
}
}
response = requests.post(
f"{coordinator_url}/v1/miners/heartbeat",
headers=headers,
json=payload
)
if response.status_code == 200:
return {
"action": "heartbeat",
"miner_id": miner_id,
"status": "✅ Heartbeat sent successfully",
"inflight": inflight,
"miner_status": status
}
else:
return {"action": "heartbeat", "status": "❌ Heartbeat failed", "error": response.text}
except Exception as e:
return {"action": "heartbeat", "status": f"❌ Error: {str(e)}"}
def poll_jobs(
miner_id: str,
api_key: str = DEFAULT_API_KEY,
coordinator_url: str = DEFAULT_COORDINATOR_URL,
max_wait: int = 30,
auto_execute: bool = False
) -> Optional[Dict]:
"""Poll for available jobs"""
try:
headers = {
"X-Api-Key": api_key,
"X-Miner-ID": miner_id,
"Content-Type": "application/json"
}
payload = {"max_wait_seconds": max_wait}
response = requests.post(
f"{coordinator_url}/v1/miners/poll",
headers=headers,
json=payload
)
if response.status_code == 200 and response.content:
job = response.json()
result = {
"action": "poll",
"miner_id": miner_id,
"status": "✅ Job assigned",
"job_id": job.get("job_id"),
"payload": job.get("payload"),
"constraints": job.get("constraints"),
"assigned_at": time.strftime("%Y-%m-%d %H:%M:%S")
}
if auto_execute:
result["auto_execution"] = "🤖 Job execution would start here"
result["execution_status"] = "Ready to execute"
return result
elif response.status_code == 204:
return {
"action": "poll",
"miner_id": miner_id,
"status": "⏸️ No jobs available",
"message": "No jobs in queue"
}
else:
return {"action": "poll", "status": "❌ Poll failed", "error": response.text}
except Exception as e:
return {"action": "poll", "status": f"❌ Error: {str(e)}"}
def submit_job_result(
job_id: str,
miner_id: str,
result: str,
api_key: str = DEFAULT_API_KEY,
coordinator_url: str = DEFAULT_COORDINATOR_URL,
success: bool = True,
duration: Optional[int] = None,
result_file: Optional[str] = None
) -> Optional[Dict]:
"""Submit job result"""
try:
headers = {
"X-Api-Key": api_key,
"X-Miner-ID": miner_id,
"Content-Type": "application/json"
}
# Load result from file if specified
if result_file:
with open(result_file, 'r') as f:
result = f.read()
payload = {
"result": result,
"success": success,
"metrics": {
"duration_ms": duration,
"completed_at": time.time()
}
}
response = requests.post(
f"{coordinator_url}/v1/miners/{job_id}/result",
headers=headers,
json=payload
)
if response.status_code == 200:
return {
"action": "result",
"job_id": job_id,
"miner_id": miner_id,
"status": "✅ Result submitted successfully",
"success": success,
"duration_ms": duration
}
else:
return {"action": "result", "status": "❌ Result submission failed", "error": response.text}
except Exception as e:
return {"action": "result", "status": f"❌ Error: {str(e)}"}
def update_capabilities(
miner_id: str,
api_key: str = DEFAULT_API_KEY,
coordinator_url: str = DEFAULT_COORDINATOR_URL,
capabilities: Optional[str] = None,
gpu_memory: Optional[int] = None,
models: Optional[list] = None,
pricing: Optional[float] = None,
concurrency: Optional[int] = None,
region: Optional[str] = None,
wallet: Optional[str] = None
) -> Optional[Dict]:
"""Update miner capabilities"""
try:
headers = {
"X-Api-Key": api_key,
"X-Miner-ID": miner_id,
"Content-Type": "application/json"
}
# Build capabilities from arguments
caps = {}
if gpu_memory:
caps["gpu_memory"] = gpu_memory
caps["gpu_memory_gb"] = gpu_memory
if models:
caps["models"] = models
caps["supported_models"] = models
if pricing:
caps["pricing_per_hour"] = pricing
caps["price_per_hour"] = pricing
# Override with capabilities JSON if provided
if capabilities:
caps.update(json.loads(capabilities))
payload = {
"capabilities": caps,
"concurrency": concurrency,
"region": region
}
if wallet:
payload["wallet_address"] = wallet
response = requests.put(
f"{coordinator_url}/v1/miners/{miner_id}/capabilities",
headers=headers,
json=payload
)
if response.status_code == 200:
return {
"action": "update",
"miner_id": miner_id,
"status": "✅ Capabilities updated successfully",
"updated_capabilities": caps
}
else:
return {"action": "update", "status": "❌ Update failed", "error": response.text}
except Exception as e:
return {"action": "update", "status": f"❌ Error: {str(e)}"}
def check_earnings(
miner_id: str,
api_key: str = DEFAULT_API_KEY,
coordinator_url: str = DEFAULT_COORDINATOR_URL,
period: str = "all"
) -> Optional[Dict]:
"""Check miner earnings (placeholder for payment integration)"""
try:
# This would integrate with payment system when implemented
return {
"action": "earnings",
"miner_id": miner_id,
"period": period,
"status": "📊 Earnings calculation",
"total_earnings": 0.0,
"jobs_completed": 0,
"average_payment": 0.0,
"note": "Payment integration coming soon"
}
except Exception as e:
return {"action": "earnings", "status": f"❌ Error: {str(e)}"}
def list_marketplace_offers(
miner_id: Optional[str] = None,
region: Optional[str] = None,
api_key: str = DEFAULT_API_KEY,
coordinator_url: str = DEFAULT_COORDINATOR_URL
) -> Optional[Dict]:
"""List marketplace offers"""
try:
admin_headers = {"X-Api-Key": api_key.replace("miner_", "admin_")}
params = {}
if region:
params["region"] = region
response = requests.get(
f"{coordinator_url}/v1/marketplace/miner-offers",
headers=admin_headers,
params=params
)
if response.status_code == 200:
offers = response.json()
# Filter by miner if specified
if miner_id:
offers = [o for o in offers if miner_id in str(o).lower()]
return {
"action": "marketplace_list",
"status": "✅ Offers retrieved",
"offers": offers,
"count": len(offers),
"region_filter": region,
"miner_filter": miner_id
}
else:
return {"action": "marketplace_list", "status": "❌ Failed to get offers", "error": response.text}
except Exception as e:
return {"action": "marketplace_list", "status": f"❌ Error: {str(e)}"}
def create_marketplace_offer(
miner_id: str,
price: float,
api_key: str = DEFAULT_API_KEY,
coordinator_url: str = DEFAULT_COORDINATOR_URL,
capacity: int = 1,
region: Optional[str] = None
) -> Optional[Dict]:
"""Create marketplace offer"""
try:
admin_headers = {"X-Api-Key": api_key.replace("miner_", "admin_")}
payload = {
"miner_id": miner_id,
"price": price,
"capacity": capacity,
"region": region
}
response = requests.post(
f"{coordinator_url}/v1/marketplace/offers",
headers=admin_headers,
json=payload
)
if response.status_code == 200:
return {
"action": "marketplace_create",
"miner_id": miner_id,
"status": "✅ Offer created successfully",
"price": price,
"capacity": capacity,
"region": region
}
else:
return {"action": "marketplace_create", "status": "❌ Offer creation failed", "error": response.text}
except Exception as e:
return {"action": "marketplace_create", "status": f"❌ Error: {str(e)}"}
# Main function for CLI integration
def miner_cli_dispatcher(action: str, **kwargs) -> Optional[Dict]:
"""Main dispatcher for miner management CLI commands"""
actions = {
"register": register_miner,
"status": get_miner_status,
"heartbeat": send_heartbeat,
"poll": poll_jobs,
"result": submit_job_result,
"update": update_capabilities,
"earnings": check_earnings,
"marketplace_list": list_marketplace_offers,
"marketplace_create": create_marketplace_offer
}
if action in actions:
return actions[action](**kwargs)
else:
return {
"action": action,
"status": f"❌ Unknown action. Available: {', '.join(actions.keys())}"
}
if __name__ == "__main__":
# Test the module
print("🚀 AITBC Miner Management Module")
print("Available functions:")
for func in [register_miner, get_miner_status, send_heartbeat, poll_jobs,
submit_job_result, update_capabilities, check_earnings,
list_marketplace_offers, create_marketplace_offer]:
print(f" - {func.__name__}")