```
chore: enhance .gitignore and remove obsolete documentation files - Reorganize .gitignore with categorized sections for better maintainability - Add comprehensive ignore patterns for Python, Node.js, databases, logs, and build artifacts - Add project-specific ignore rules for coordinator, explorer, and deployment files - Remove outdated documentation: BITCOIN-WALLET-SETUP.md, LOCAL_ASSETS_SUMMARY.md, README-CONTAINER-DEPLOYMENT.md, README-DOMAIN-DEPLOYMENT.md ```
This commit is contained in:
126
scripts/aitbc-cli.sh
Executable file
126
scripts/aitbc-cli.sh
Executable file
@@ -0,0 +1,126 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
CLI_PY="$ROOT_DIR/cli/client.py"
|
||||
|
||||
AITBC_URL="${AITBC_URL:-http://127.0.0.1:18000}"
|
||||
CLIENT_KEY="${CLIENT_KEY:-REDACTED_CLIENT_KEY}"
|
||||
ADMIN_KEY="${ADMIN_KEY:-REDACTED_ADMIN_KEY}"
|
||||
MINER_KEY="${MINER_KEY:-REDACTED_MINER_KEY}"
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
AITBC CLI wrapper
|
||||
|
||||
Usage:
|
||||
aitbc-cli.sh submit <type> [--prompt TEXT] [--model NAME] [--ttl SECONDS]
|
||||
aitbc-cli.sh status <job_id>
|
||||
aitbc-cli.sh browser [--block-limit N] [--tx-limit N] [--receipt-limit N] [--job-id ID]
|
||||
aitbc-cli.sh blocks [--limit N]
|
||||
aitbc-cli.sh receipts [--limit N] [--job-id ID]
|
||||
aitbc-cli.sh cancel <job_id>
|
||||
aitbc-cli.sh admin-miners
|
||||
aitbc-cli.sh admin-jobs
|
||||
aitbc-cli.sh admin-stats
|
||||
aitbc-cli.sh admin-cancel-running
|
||||
aitbc-cli.sh health
|
||||
|
||||
Environment overrides:
|
||||
AITBC_URL (default: http://127.0.0.1:18000)
|
||||
CLIENT_KEY (default: REDACTED_CLIENT_KEY)
|
||||
ADMIN_KEY (default: REDACTED_ADMIN_KEY)
|
||||
MINER_KEY (default: REDACTED_MINER_KEY)
|
||||
EOF
|
||||
}
|
||||
|
||||
if [[ $# -lt 1 ]]; then
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cmd="$1"
|
||||
shift
|
||||
|
||||
case "$cmd" in
|
||||
submit)
|
||||
python3 "$CLI_PY" --url "$AITBC_URL" --api-key "$CLIENT_KEY" submit "$@"
|
||||
;;
|
||||
status)
|
||||
python3 "$CLI_PY" --url "$AITBC_URL" --api-key "$CLIENT_KEY" status "$@"
|
||||
;;
|
||||
browser)
|
||||
python3 "$CLI_PY" --url "$AITBC_URL" --api-key "$CLIENT_KEY" browser "$@"
|
||||
;;
|
||||
blocks)
|
||||
python3 "$CLI_PY" --url "$AITBC_URL" --api-key "$CLIENT_KEY" blocks "$@"
|
||||
;;
|
||||
receipts)
|
||||
limit=10
|
||||
job_id=""
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--limit)
|
||||
limit="$2"
|
||||
shift 2
|
||||
;;
|
||||
--job-id)
|
||||
job_id="$2"
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option: $1" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
if [[ -n "$job_id" ]]; then
|
||||
curl -sS "$AITBC_URL/v1/explorer/receipts?limit=${limit}&job_id=${job_id}"
|
||||
else
|
||||
curl -sS "$AITBC_URL/v1/explorer/receipts?limit=${limit}"
|
||||
fi
|
||||
;;
|
||||
cancel)
|
||||
if [[ $# -lt 1 ]]; then
|
||||
echo "Usage: aitbc-cli.sh cancel <job_id>" >&2
|
||||
exit 1
|
||||
fi
|
||||
job_id="$1"
|
||||
curl -sS -X POST -H "X-Api-Key: ${CLIENT_KEY}" "$AITBC_URL/v1/jobs/${job_id}/cancel"
|
||||
;;
|
||||
admin-miners)
|
||||
curl -sS -H "X-Api-Key: ${ADMIN_KEY}" "$AITBC_URL/v1/admin/miners"
|
||||
;;
|
||||
admin-jobs)
|
||||
curl -sS -H "X-Api-Key: ${ADMIN_KEY}" "$AITBC_URL/v1/admin/jobs"
|
||||
;;
|
||||
admin-stats)
|
||||
curl -sS -H "X-Api-Key: ${ADMIN_KEY}" "$AITBC_URL/v1/admin/stats"
|
||||
;;
|
||||
admin-cancel-running)
|
||||
echo "Fetching running jobs..."
|
||||
running_jobs=$(curl -sS -H "X-Api-Key: ${ADMIN_KEY}" "$AITBC_URL/v1/admin/jobs" | jq -r '.[] | select(.state == "running") | .id')
|
||||
if [[ -z "$running_jobs" ]]; then
|
||||
echo "No running jobs found."
|
||||
else
|
||||
count=0
|
||||
for job_id in $running_jobs; do
|
||||
echo "Cancelling job: $job_id"
|
||||
curl -sS -X POST -H "X-Api-Key: ${CLIENT_KEY}" "$AITBC_URL/v1/jobs/${job_id}/cancel" > /dev/null
|
||||
((count++))
|
||||
done
|
||||
echo "Cancelled $count running jobs."
|
||||
fi
|
||||
;;
|
||||
health)
|
||||
curl -sS "$AITBC_URL/v1/health"
|
||||
;;
|
||||
help|-h|--help)
|
||||
usage
|
||||
;;
|
||||
*)
|
||||
echo "Unknown command: $cmd" >&2
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
43
scripts/apply_bootstrap_genesis.sh
Normal file
43
scripts/apply_bootstrap_genesis.sh
Normal file
@@ -0,0 +1,43 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "=== AITBC Bootstrap Genesis Setup ==="
|
||||
echo ""
|
||||
|
||||
# Stop the blockchain node
|
||||
echo "1. Stopping blockchain node..."
|
||||
sudo systemctl stop aitbc-node
|
||||
|
||||
# Backup current data
|
||||
echo "2. Backing up current blockchain data..."
|
||||
sudo mv /root/aitbc/apps/blockchain-node/data/devnet/db.sqlite /root/aitbc/apps/blockchain-node/data/devnet/db.sqlite.backup.$(date +%s) 2>/dev/null || true
|
||||
|
||||
# Copy new genesis
|
||||
echo "3. Applying bootstrap genesis..."
|
||||
sudo cp /root/aitbc/apps/blockchain-node/data/genesis_with_bootstrap.json /root/aitbc/apps/blockchain-node/data/devnet/genesis.json
|
||||
|
||||
# Reset database
|
||||
echo "4. Resetting blockchain database..."
|
||||
sudo rm -f /root/aitbc/apps/blockchain-node/data/devnet/db.sqlite
|
||||
|
||||
# Restart blockchain node
|
||||
echo "5. Restarting blockchain node..."
|
||||
sudo systemctl start aitbc-node
|
||||
|
||||
# Wait for node to start
|
||||
echo "6. Waiting for node to initialize..."
|
||||
sleep 5
|
||||
|
||||
# Verify treasury balance
|
||||
echo "7. Verifying treasury balance..."
|
||||
curl -s http://localhost:9080/rpc/getBalance/aitbcexchange00000000000000000000000000000000 | jq
|
||||
|
||||
echo ""
|
||||
echo "=== Bootstrap Complete! ==="
|
||||
echo "Treasury should now have 10,000,000 AITBC"
|
||||
echo ""
|
||||
echo "Initial Distribution:"
|
||||
echo "- Exchange Treasury: 10,000,000 AITBC (47.6%)"
|
||||
echo "- Community Faucet: 1,000,000 AITBC (4.8%)"
|
||||
echo "- Team Fund: 2,000,000 AITBC (9.5%)"
|
||||
echo "- Early Investors: 5,000,000 AITBC (23.8%)"
|
||||
echo "- Ecosystem Fund: 3,000,000 AITBC (14.3%)"
|
||||
75
scripts/assign_proposer.py
Normal file
75
scripts/assign_proposer.py
Normal file
@@ -0,0 +1,75 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Script to assign a proposer to a block by polling for it
|
||||
"""
|
||||
|
||||
import httpx
|
||||
import json
|
||||
|
||||
# Configuration
|
||||
COORDINATOR_URL = "http://localhost:8001"
|
||||
MINER_API_KEY = "REDACTED_MINER_KEY"
|
||||
MINER_ID = "localhost-gpu-miner"
|
||||
|
||||
def assign_proposer_to_latest_block():
|
||||
"""Poll for the latest unassigned job to become the proposer"""
|
||||
|
||||
# First register the miner
|
||||
print("📝 Registering miner...")
|
||||
register_response = httpx.post(
|
||||
f"{COORDINATOR_URL}/v1/miners/register?miner_id={MINER_ID}",
|
||||
headers={
|
||||
"Content-Type": "application/json",
|
||||
"X-Api-Key": MINER_API_KEY
|
||||
},
|
||||
json={
|
||||
"capabilities": {
|
||||
"gpu": {"model": "RTX 4060 Ti", "memory_gb": 16}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
if register_response.status_code != 200:
|
||||
print(f"❌ Registration failed: {register_response.text}")
|
||||
return
|
||||
|
||||
print("✅ Miner registered")
|
||||
|
||||
# Poll for a job
|
||||
print("\n🔍 Polling for jobs...")
|
||||
poll_response = httpx.post(
|
||||
f"{COORDINATOR_URL}/v1/miners/poll",
|
||||
headers={
|
||||
"Content-Type": "application/json",
|
||||
"X-Api-Key": MINER_API_KEY
|
||||
},
|
||||
json={"max_wait_seconds": 1}
|
||||
)
|
||||
|
||||
if poll_response.status_code == 200:
|
||||
job = poll_response.json()
|
||||
print(f"✅ Received job: {job['job_id']}")
|
||||
print(f" This job is now assigned to miner: {MINER_ID}")
|
||||
|
||||
# Check the block
|
||||
print("\n📦 Checking block...")
|
||||
blocks_response = httpx.get(f"{COORDINATOR_URL}/v1/explorer/blocks")
|
||||
|
||||
if blocks_response.status_code == 200:
|
||||
blocks = blocks_response.json()
|
||||
for block in blocks['items']:
|
||||
if block['hash'] == job['job_id']:
|
||||
print(f"✅ Block updated!")
|
||||
print(f" Height: {block['height']}")
|
||||
print(f" Hash: {block['hash']}")
|
||||
print(f" Proposer: {block['proposer']}")
|
||||
break
|
||||
elif poll_response.status_code == 204:
|
||||
print("ℹ️ No jobs available to poll")
|
||||
else:
|
||||
print(f"❌ Poll failed: {poll_response.text}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("🎯 Assign Proposer to Latest Block")
|
||||
print("=" * 40)
|
||||
assign_proposer_to_latest_block()
|
||||
12
scripts/check_coordinator_proxy.sh
Executable file
12
scripts/check_coordinator_proxy.sh
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
HEALTH_URL="http://127.0.0.1:18000/v1/health"
|
||||
|
||||
if curl -fsS --max-time 5 "$HEALTH_URL" >/dev/null; then
|
||||
echo "Coordinator proxy healthy: $HEALTH_URL"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Coordinator proxy health check FAILED: $HEALTH_URL" >&2
|
||||
exit 1
|
||||
109
scripts/deploy/container-deploy.py
Normal file
109
scripts/deploy/container-deploy.py
Normal file
@@ -0,0 +1,109 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Deploy AITBC services to incus container
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import time
|
||||
import sys
|
||||
|
||||
def run_command(cmd, container=None):
|
||||
"""Run command locally or in container"""
|
||||
if container:
|
||||
cmd = f"incus exec {container} -- {cmd}"
|
||||
print(f"Running: {cmd}")
|
||||
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
|
||||
if result.returncode != 0:
|
||||
print(f"Error: {result.stderr}")
|
||||
return False
|
||||
return True
|
||||
|
||||
def deploy_to_container():
|
||||
container = "aitbc"
|
||||
container_ip = "10.1.223.93"
|
||||
|
||||
print("🚀 Deploying AITBC services to container...")
|
||||
|
||||
# Stop local services
|
||||
print("\n📋 Stopping local services...")
|
||||
subprocess.run("sudo fuser -k 8000/tcp 2>/dev/null || true", shell=True)
|
||||
subprocess.run("sudo fuser -k 9080/tcp 2>/dev/null || true", shell=True)
|
||||
subprocess.run("pkill -f 'marketplace-ui' 2>/dev/null || true", shell=True)
|
||||
subprocess.run("pkill -f 'trade-exchange' 2>/dev/null || true", shell=True)
|
||||
|
||||
# Copy project to container
|
||||
print("\n📁 Copying project to container...")
|
||||
subprocess.run(f"incus file push -r /home/oib/windsurf/aitbc {container}/home/oib/", shell=True)
|
||||
|
||||
# Setup Python environment in container
|
||||
print("\n🐍 Setting up Python environment...")
|
||||
run_command("cd /home/oib/aitbc && python3 -m venv .venv", container)
|
||||
run_command("cd /home/oib/aitbc && source .venv/bin/activate && pip install fastapi uvicorn httpx sqlmodel", container)
|
||||
|
||||
# Install dependencies
|
||||
print("\n📦 Installing dependencies...")
|
||||
run_command("cd /home/oib/aitbc/apps/coordinator-api && source ../../.venv/bin/activate && pip install -e .", container)
|
||||
run_command("cd /home/oib/aitbc/apps/blockchain-node && source ../../.venv/bin/activate && pip install -e .", container)
|
||||
|
||||
# Create startup script
|
||||
print("\n🔧 Creating startup script...")
|
||||
startup_script = """#!/bin/bash
|
||||
cd /home/oib/aitbc
|
||||
|
||||
# Start blockchain node
|
||||
echo "Starting blockchain node..."
|
||||
cd apps/blockchain-node
|
||||
source ../../.venv/bin/activate
|
||||
python -m uvicorn aitbc_chain.app:app --host 0.0.0.0 --port 9080 &
|
||||
NODE_PID=$!
|
||||
|
||||
# Start coordinator API
|
||||
echo "Starting coordinator API..."
|
||||
cd ../coordinator-api
|
||||
source ../../.venv/bin/activate
|
||||
python -m uvicorn src.app.main:app --host 0.0.0.0 --port 8000 &
|
||||
COORD_PID=$!
|
||||
|
||||
# Start marketplace UI
|
||||
echo "Starting marketplace UI..."
|
||||
cd ../marketplace-ui
|
||||
python server.py --port 3001 &
|
||||
MARKET_PID=$!
|
||||
|
||||
# Start trade exchange
|
||||
echo "Starting trade exchange..."
|
||||
cd ../trade-exchange
|
||||
python server.py --port 3002 &
|
||||
EXCHANGE_PID=$!
|
||||
|
||||
echo "Services started!"
|
||||
echo "Blockchain: http://10.1.223.93:9080"
|
||||
echo "API: http://10.1.223.93:8000"
|
||||
echo "Marketplace: http://10.1.223.93:3001"
|
||||
echo "Exchange: http://10.1.223.93:3002"
|
||||
|
||||
# Wait for services
|
||||
wait $NODE_PID $COORD_PID $MARKET_PID $EXCHANGE_PID
|
||||
"""
|
||||
|
||||
# Write startup script to container
|
||||
with open('/tmp/start_aitbc.sh', 'w') as f:
|
||||
f.write(startup_script)
|
||||
|
||||
subprocess.run("incus file push /tmp/start_aitbc.sh aitbc/home/oib/", shell=True)
|
||||
run_command("chmod +x /home/oib/start_aitbc.sh", container)
|
||||
|
||||
# Start services
|
||||
print("\n🚀 Starting AITBC services...")
|
||||
run_command("/home/oib/start_aitbc.sh", container)
|
||||
|
||||
print(f"\n✅ Services deployed to container!")
|
||||
print(f"\n📋 Access URLs:")
|
||||
print(f" 🌐 Container IP: {container_ip}")
|
||||
print(f" 📊 Marketplace: http://{container_ip}:3001")
|
||||
print(f" 💱 Trade Exchange: http://{container_ip}:3002")
|
||||
print(f" 🔗 API: http://{container_ip}:8000")
|
||||
print(f" ⛓️ Blockchain: http://{container_ip}:9080")
|
||||
|
||||
if __name__ == "__main__":
|
||||
deploy_to_container()
|
||||
88
scripts/deploy/deploy-domain.sh
Executable file
88
scripts/deploy/deploy-domain.sh
Executable file
@@ -0,0 +1,88 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Deploy AITBC services to domain https://aitbc.bubuit.net
|
||||
|
||||
set -e
|
||||
|
||||
DOMAIN="aitbc.bubuit.net"
|
||||
CONTAINER="aitbc"
|
||||
|
||||
echo "🚀 Deploying AITBC services to https://$DOMAIN"
|
||||
echo ""
|
||||
|
||||
# Colors
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
print_status() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
# Stop local services
|
||||
print_status "Stopping local services..."
|
||||
sudo fuser -k 8000/tcp 2>/dev/null || true
|
||||
sudo fuser -k 9080/tcp 2>/dev/null || true
|
||||
sudo fuser -k 3001/tcp 2>/dev/null || true
|
||||
sudo fuser -k 3002/tcp 2>/dev/null || true
|
||||
|
||||
# Deploy to container
|
||||
print_status "Deploying to container..."
|
||||
python /home/oib/windsurf/aitbc/container-deploy.py
|
||||
|
||||
# Copy nginx config to container
|
||||
print_status "Configuring nginx for domain..."
|
||||
incus file push /home/oib/windsurf/aitbc/nginx-aitbc.conf $CONTAINER/etc/nginx/sites-available/aitbc
|
||||
|
||||
# Enable site
|
||||
incus exec $CONTAINER -- ln -sf /etc/nginx/sites-available/aitbc /etc/nginx/sites-enabled/
|
||||
incus exec $CONTAINER -- rm -f /etc/nginx/sites-enabled/default
|
||||
|
||||
# Test nginx config
|
||||
incus exec $CONTAINER -- nginx -t
|
||||
|
||||
# Reload nginx
|
||||
incus exec $CONTAINER -- systemctl reload nginx
|
||||
|
||||
# Install SSL certificate (Let's Encrypt)
|
||||
print_warning "SSL Certificate Setup:"
|
||||
echo "1. Ensure port 80/443 are forwarded to container IP (10.1.223.93)"
|
||||
echo "2. Run certbot in container:"
|
||||
echo " incus exec $CONTAINER -- certbot --nginx -d $DOMAIN"
|
||||
echo ""
|
||||
|
||||
# Update UIs to use correct API endpoints
|
||||
print_status "Updating API endpoints..."
|
||||
|
||||
# Update marketplace API base URL
|
||||
incus exec $CONTAINER -- sed -i "s|http://127.0.0.1:8000|https://$DOMAIN/api|g" /home/oib/aitbc/apps/marketplace-ui/index.html
|
||||
|
||||
# Update exchange API endpoints
|
||||
incus exec $CONTAINER -- sed -i "s|http://127.0.0.1:8000|https://$DOMAIN/api|g" /home/oib/aitbc/apps/trade-exchange/index.html
|
||||
incus exec $CONTAINER -- sed -i "s|http://127.0.0.1:9080|https://$DOMAIN/rpc|g" /home/oib/aitbc/apps/trade-exchange/index.html
|
||||
|
||||
# Restart services to apply changes
|
||||
print_status "Restarting services..."
|
||||
incus exec $CONTAINER -- pkill -f "server.py"
|
||||
sleep 2
|
||||
incus exec $CONTAINER -- /home/oib/start_aitbc.sh
|
||||
|
||||
echo ""
|
||||
print_status "✅ Deployment complete!"
|
||||
echo ""
|
||||
echo "📋 Service URLs:"
|
||||
echo " 🌐 Domain: https://$DOMAIN"
|
||||
echo " 📊 Marketplace: https://$DOMAIN/Marketplace"
|
||||
echo " 💱 Trade Exchange: https://$DOMAIN/Exchange"
|
||||
echo " 🔗 API: https://$DOMAIN/api"
|
||||
echo " ⛓️ Blockchain RPC: https://$DOMAIN/rpc"
|
||||
echo ""
|
||||
echo "📝 Next Steps:"
|
||||
echo "1. Forward ports 80/443 to container IP (10.1.223.93)"
|
||||
echo "2. Install SSL certificate:"
|
||||
echo " incus exec $CONTAINER -- certbot --nginx -d $DOMAIN"
|
||||
echo "3. Test services at the URLs above"
|
||||
74
scripts/deploy/deploy-exchange.sh
Executable file
74
scripts/deploy/deploy-exchange.sh
Executable file
@@ -0,0 +1,74 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Deploy AITBC Trade Exchange to the server
|
||||
|
||||
set -e
|
||||
|
||||
SERVER="root@10.1.223.93"
|
||||
EXCHANGE_DIR="/root/aitbc/apps/trade-exchange"
|
||||
|
||||
echo "🚀 Deploying AITBC Trade Exchange"
|
||||
echo "=================================="
|
||||
echo "Server: $SERVER"
|
||||
echo ""
|
||||
|
||||
# Colors
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
print_status() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
# Test SSH connection
|
||||
print_status "Testing SSH connection..."
|
||||
ssh $SERVER "hostname && ip a show eth0 | grep inet"
|
||||
|
||||
# Copy updated files
|
||||
print_status "Copying updated Exchange files..."
|
||||
scp /home/oib/windsurf/aitbc/apps/trade-exchange/index.html $SERVER:$EXCHANGE_DIR/
|
||||
scp /home/oib/windsurf/aitbc/apps/trade-exchange/server.py $SERVER:$EXCHANGE_DIR/
|
||||
|
||||
# Ensure assets are available
|
||||
print_status "Ensuring assets directory exists..."
|
||||
ssh $SERVER "mkdir -p /var/www/aitbc.bubuit.net/assets"
|
||||
ssh $SERVER "mkdir -p /var/www/aitbc.bubuit.net/assets/css"
|
||||
ssh $SERVER "mkdir -p /var/www/aitbc.bubuit.net/assets/js"
|
||||
|
||||
# Copy assets if they don't exist
|
||||
print_status "Copying assets if needed..."
|
||||
if ! ssh $SERVER "test -f /var/www/aitbc.bubuit.net/assets/css/aitbc.css"; then
|
||||
scp -r /home/oib/windsurf/aitbc/assets/* $SERVER:/var/www/aitbc.bubuit.net/assets/
|
||||
fi
|
||||
|
||||
# Restart the exchange service
|
||||
print_status "Restarting Trade Exchange service..."
|
||||
ssh $SERVER "systemctl restart aitbc-exchange"
|
||||
|
||||
# Wait for service to start
|
||||
print_status "Waiting for service to start..."
|
||||
sleep 5
|
||||
|
||||
# Check service status
|
||||
print_status "Checking service status..."
|
||||
ssh $SERVER "systemctl status aitbc-exchange --no-pager -l | head -10"
|
||||
|
||||
# Test the endpoint
|
||||
print_status "Testing Exchange endpoint..."
|
||||
ssh $SERVER "curl -s http://127.0.0.1:3002/ | head -c 100"
|
||||
echo ""
|
||||
|
||||
echo ""
|
||||
print_status "✅ Exchange deployment complete!"
|
||||
echo ""
|
||||
echo "📋 URLs:"
|
||||
echo " 🌐 IP: http://10.1.223.93/Exchange"
|
||||
echo " 🔒 Domain: https://aitbc.bubuit.net/Exchange"
|
||||
echo ""
|
||||
echo "🔍 To check logs:"
|
||||
echo " ssh $SERVER 'journalctl -u aitbc-exchange -f'"
|
||||
66
scripts/deploy/deploy-explorer.sh
Executable file
66
scripts/deploy/deploy-explorer.sh
Executable file
@@ -0,0 +1,66 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Deploy AITBC Explorer to the server
|
||||
|
||||
set -e
|
||||
|
||||
SERVER="root@10.1.223.93"
|
||||
EXPLORER_DIR="/root/aitbc/apps/explorer-web"
|
||||
NGINX_CONFIG="/etc/nginx/sites-available/aitbc"
|
||||
|
||||
echo "🚀 Deploying AITBC Explorer to Server"
|
||||
echo "====================================="
|
||||
echo "Server: $SERVER"
|
||||
echo ""
|
||||
|
||||
# Colors
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
print_status() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
# Build the explorer locally first
|
||||
print_status "Building explorer locally..."
|
||||
cd /home/oib/windsurf/aitbc/apps/explorer-web
|
||||
npm run build
|
||||
|
||||
# Copy built files to server
|
||||
print_status "Copying explorer build to server..."
|
||||
scp -r dist $SERVER:$EXPLORER_DIR/
|
||||
|
||||
# Update nginx config to include explorer
|
||||
print_status "Updating nginx configuration..."
|
||||
|
||||
# Backup current config
|
||||
ssh $SERVER "cp $NGINX_CONFIG ${NGINX_CONFIG}.backup"
|
||||
|
||||
# Add explorer location to nginx config
|
||||
ssh $SERVER "sed -i '/# Health endpoint/i\\
|
||||
# Explorer\\
|
||||
location /explorer/ {\\
|
||||
alias /root/aitbc/apps/explorer-web/dist/;\\
|
||||
try_files \$uri \$uri/ /explorer/index.html;\\
|
||||
}\\
|
||||
\\
|
||||
# Explorer mock data\\
|
||||
location /explorer/mock/ {\\
|
||||
alias /root/aitbc/apps/explorer-web/public/mock/;\\
|
||||
}\\
|
||||
' $NGINX_CONFIG"
|
||||
|
||||
# Test and reload nginx
|
||||
print_status "Testing and reloading nginx..."
|
||||
ssh $SERVER "nginx -t && systemctl reload nginx"
|
||||
|
||||
print_status "✅ Explorer deployment complete!"
|
||||
echo ""
|
||||
echo "📋 Explorer URL:"
|
||||
echo " 🌐 Explorer: https://aitbc.bubuit.net/explorer/"
|
||||
echo ""
|
||||
55
scripts/deploy/deploy-production.sh
Normal file
55
scripts/deploy/deploy-production.sh
Normal file
@@ -0,0 +1,55 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "🚀 Deploying AITBC for Production..."
|
||||
|
||||
# 1. Setup production assets
|
||||
echo "📦 Setting up production assets..."
|
||||
bash setup-production-assets.sh
|
||||
|
||||
# 2. Copy assets to server
|
||||
echo "📋 Copying assets to server..."
|
||||
scp -r assets/ aitbc:/var/www/html/
|
||||
|
||||
# 3. Update Nginx configuration
|
||||
echo "⚙️ Updating Nginx configuration..."
|
||||
ssh aitbc "cat >> /etc/nginx/sites-available/aitbc.conf << 'EOF'
|
||||
|
||||
# Serve production assets
|
||||
location /assets/ {
|
||||
alias /var/www/html/assets/;
|
||||
expires 1y;
|
||||
add_header Cache-Control \"public, immutable\";
|
||||
add_header X-Content-Type-Options nosniff;
|
||||
|
||||
# Gzip compression
|
||||
gzip on;
|
||||
gzip_types text/css application/javascript image/svg+xml;
|
||||
}
|
||||
|
||||
# Security headers
|
||||
add_header Referrer-Policy \"strict-origin-when-cross-origin\" always;
|
||||
add_header X-Frame-Options \"SAMEORIGIN\" always;
|
||||
add_header X-Content-Type-Options \"nosniff\" always;
|
||||
EOF"
|
||||
|
||||
# 4. Reload Nginx
|
||||
echo "🔄 Reloading Nginx..."
|
||||
ssh aitbc "nginx -t && systemctl reload nginx"
|
||||
|
||||
# 5. Update Exchange page to use production assets
|
||||
echo "🔄 Updating Exchange page..."
|
||||
scp apps/trade-exchange/index.prod.html aitbc:/root/aitbc/apps/trade-exchange/index.html
|
||||
|
||||
# 6. Update Marketplace page
|
||||
echo "🔄 Updating Marketplace page..."
|
||||
sed -i 's|https://cdn.tailwindcss.com|/assets/js/tailwind.js|g' apps/marketplace-ui/index.html
|
||||
sed -i 's|https://unpkg.com/axios/dist/axios.min.js|/assets/js/axios.min.js|g' apps/marketplace-ui/index.html
|
||||
sed -i 's|https://unpkg.com/lucide@latest|/assets/js/lucide.js|g' apps/marketplace-ui/index.html
|
||||
scp apps/marketplace-ui/index.html aitbc:/root/aitbc/apps/marketplace-ui/
|
||||
|
||||
echo "✅ Production deployment complete!"
|
||||
echo ""
|
||||
echo "📝 Next steps:"
|
||||
echo "1. Restart services: ssh aitbc 'systemctl restart aitbc-exchange aitbc-marketplace-ui'"
|
||||
echo "2. Clear browser cache"
|
||||
echo "3. Test all pages"
|
||||
253
scripts/deploy/deploy-to-container.sh
Executable file
253
scripts/deploy/deploy-to-container.sh
Executable file
@@ -0,0 +1,253 @@
|
||||
#!/bin/bash
|
||||
|
||||
# AITBC Services Deployment to Incus Container
|
||||
# This script deploys all AITBC services to the 'aitbc' container
|
||||
|
||||
set -e
|
||||
|
||||
CONTAINER_NAME="aitbc"
|
||||
CONTAINER_IP="10.1.223.93"
|
||||
PROJECT_DIR="/home/oib/windsurf/aitbc"
|
||||
|
||||
echo "🚀 Deploying AITBC services to container: $CONTAINER_NAME"
|
||||
echo "Container IP: $CONTAINER_IP"
|
||||
echo ""
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Function to print colored output
|
||||
print_status() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
print_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# Stop local services
|
||||
print_status "Stopping local AITBC services..."
|
||||
sudo fuser -k 8000/tcp 2>/dev/null || true
|
||||
sudo fuser -k 9080/tcp 2>/dev/null || true
|
||||
sudo fuser -k 3001/tcp 2>/dev/null || true
|
||||
sudo fuser -k 3002/tcp 2>/dev/null || true
|
||||
pkill -f "aitbc_chain.app" 2>/dev/null || true
|
||||
pkill -f "marketplace-ui" 2>/dev/null || true
|
||||
pkill -f "trade-exchange" 2>/dev/null || true
|
||||
|
||||
# Copy project to container
|
||||
print_status "Copying AITBC project to container..."
|
||||
incus file push -r $PROJECT_DIR $CONTAINER_NAME/home/oib/
|
||||
|
||||
# Setup container environment
|
||||
print_status "Setting up container environment..."
|
||||
incus exec $CONTAINER_NAME -- bash -c "
|
||||
cd /home/oib/aitbc
|
||||
python -m venv .venv
|
||||
source .venv/bin/activate
|
||||
pip install --upgrade pip
|
||||
"
|
||||
|
||||
# Install dependencies for each service
|
||||
print_status "Installing dependencies..."
|
||||
|
||||
# Coordinator API
|
||||
print_status "Installing Coordinator API dependencies..."
|
||||
incus exec $CONTAINER_NAME -- bash -c "
|
||||
cd /home/oib/aitbc/apps/coordinator-api
|
||||
source ../.venv/bin/activate
|
||||
pip install -e .
|
||||
pip install fastapi uvicorn
|
||||
"
|
||||
|
||||
# Blockchain Node
|
||||
print_status "Installing Blockchain Node dependencies..."
|
||||
incus exec $CONTAINER_NAME -- bash -c "
|
||||
cd /home/oib/aitbc/apps/blockchain-node
|
||||
source ../.venv/bin/activate
|
||||
pip install -e .
|
||||
pip install fastapi uvicorn
|
||||
"
|
||||
|
||||
# Create systemd service files
|
||||
print_status "Creating systemd services..."
|
||||
|
||||
# Coordinator API service
|
||||
incus exec $CONTAINER_NAME -- tee /etc/systemd/system/aitbc-coordinator.service > /dev/null <<EOF
|
||||
[Unit]
|
||||
Description=AITBC Coordinator API
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=exec
|
||||
User=oib
|
||||
Group=oib
|
||||
WorkingDirectory=/home/oib/aitbc/apps/coordinator-api
|
||||
Environment=PATH=/home/oib/aitbc/.venv/bin
|
||||
ExecStart=/home/oib/aitbc/.venv/bin/python -m uvicorn src.app.main:app --host 0.0.0.0 --port 8000
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
# Blockchain Node service
|
||||
incus exec $CONTAINER_NAME -- tee /etc/systemd/system/aitbc-blockchain.service > /dev/null <<EOF
|
||||
[Unit]
|
||||
Description=AITBC Blockchain Node
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=exec
|
||||
User=oib
|
||||
Group=oib
|
||||
WorkingDirectory=/home/oib/aitbc/apps/blockchain-node
|
||||
Environment=PATH=/home/oib/aitbc/.venv/bin
|
||||
ExecStart=/home/oib/aitbc/.venv/bin/python -m uvicorn aitbc_chain.app:app --host 0.0.0.0 --port 9080
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
# Marketplace UI service
|
||||
incus exec $CONTAINER_NAME -- tee /etc/systemd/system/aitbc-marketplace.service > /dev/null <<EOF
|
||||
[Unit]
|
||||
Description=AITBC Marketplace UI
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=exec
|
||||
User=oib
|
||||
Group=oib
|
||||
WorkingDirectory=/home/oib/aitbc/apps/marketplace-ui
|
||||
Environment=PATH=/home/oib/aitbc/.venv/bin
|
||||
ExecStart=/home/oib/aitbc/.venv/bin/python server.py --port 3001
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
# Trade Exchange service
|
||||
incus exec $CONTAINER_NAME -- tee /etc/systemd/system/aitbc-exchange.service > /dev/null <<EOF
|
||||
[Unit]
|
||||
Description=AITBC Trade Exchange
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=exec
|
||||
User=oib
|
||||
Group=oib
|
||||
WorkingDirectory=/home/oib/aitbc/apps/trade-exchange
|
||||
Environment=PATH=/home/oib/aitbc/.venv/bin
|
||||
ExecStart=/home/oib/aitbc/.venv/bin/python server.py --port 3002
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
# Reload systemd and start services
|
||||
print_status "Starting AITBC services..."
|
||||
incus exec $CONTAINER_NAME -- systemctl daemon-reload
|
||||
incus exec $CONTAINER_NAME -- systemctl enable aitbc-coordinator
|
||||
incus exec $CONTAINER_NAME -- systemctl enable aitbc-blockchain
|
||||
incus exec $CONTAINER_NAME -- systemctl enable aitbc-marketplace
|
||||
incus exec $CONTAINER_NAME -- systemctl enable aitbc-exchange
|
||||
|
||||
incus exec $CONTAINER_NAME -- systemctl start aitbc-coordinator
|
||||
incus exec $CONTAINER_NAME -- systemctl start aitbc-blockchain
|
||||
incus exec $CONTAINER_NAME -- systemctl start aitbc-marketplace
|
||||
incus exec $CONTAINER_NAME -- systemctl start aitbc-exchange
|
||||
|
||||
# Wait for services to start
|
||||
print_status "Waiting for services to start..."
|
||||
sleep 10
|
||||
|
||||
# Check service status
|
||||
print_status "Checking service status..."
|
||||
incus exec $CONTAINER_NAME -- systemctl status aitbc-coordinator --no-pager -l
|
||||
incus exec $CONTAINER_NAME -- systemctl status aitbc-blockchain --no-pager -l
|
||||
incus exec $CONTAINER_NAME -- systemctl status aitbc-marketplace --no-pager -l
|
||||
incus exec $CONTAINER_NAME -- systemctl status aitbc-exchange --no-pager -l
|
||||
|
||||
# Create nginx configuration for reverse proxy
|
||||
print_status "Setting up Nginx reverse proxy..."
|
||||
incus exec $CONTAINER_NAME -- tee /etc/nginx/sites-available/aitbc > /dev/null <<EOF
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
|
||||
# Coordinator API
|
||||
location /api/ {
|
||||
proxy_pass http://127.0.0.1:8000/v1/;
|
||||
proxy_set_header Host \$host;
|
||||
proxy_set_header X-Real-IP \$remote_addr;
|
||||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto \$scheme;
|
||||
}
|
||||
|
||||
# Blockchain RPC
|
||||
location /rpc/ {
|
||||
proxy_pass http://127.0.0.1:9080/rpc/;
|
||||
proxy_set_header Host \$host;
|
||||
proxy_set_header X-Real-IP \$remote_addr;
|
||||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto \$scheme;
|
||||
}
|
||||
|
||||
# Marketplace UI
|
||||
location /marketplace/ {
|
||||
proxy_pass http://127.0.0.1:3001/;
|
||||
proxy_set_header Host \$host;
|
||||
proxy_set_header X-Real-IP \$remote_addr;
|
||||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto \$scheme;
|
||||
}
|
||||
|
||||
# Trade Exchange
|
||||
location /exchange/ {
|
||||
proxy_pass http://127.0.0.1:3002/;
|
||||
proxy_set_header Host \$host;
|
||||
proxy_set_header X-Real-IP \$remote_addr;
|
||||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto \$scheme;
|
||||
}
|
||||
|
||||
# Default redirect to marketplace
|
||||
location / {
|
||||
return 301 /marketplace/;
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
# Enable nginx site
|
||||
incus exec $CONTAINER_NAME -- ln -sf /etc/nginx/sites-available/aitbc /etc/nginx/sites-enabled/
|
||||
incus exec $CONTAINER_NAME -- rm -f /etc/nginx/sites-enabled/default
|
||||
incus exec $CONTAINER_NAME -- nginx -t && incus exec $CONTAINER_NAME -- systemctl reload nginx
|
||||
|
||||
# Print access information
|
||||
echo ""
|
||||
print_status "✅ AITBC services deployed successfully!"
|
||||
echo ""
|
||||
echo "📋 Service URLs:"
|
||||
echo " 🌐 Public IP: $CONTAINER_IP"
|
||||
echo " 📊 Marketplace: http://$CONTAINER_IP/marketplace/"
|
||||
echo " 💱 Trade Exchange: http://$CONTAINER_IP/exchange/"
|
||||
echo " 🔗 API: http://$CONTAINER_IP/api/"
|
||||
echo " ⛓️ Blockchain RPC: http://$CONTAINER_IP/rpc/"
|
||||
echo ""
|
||||
print_status "To check logs: incus exec $CONTAINER_NAME -- journalctl -u aitbc-coordinator -f"
|
||||
print_status "To restart services: incus exec $CONTAINER_NAME -- systemctl restart aitbc-*"
|
||||
241
scripts/deploy/deploy-to-server.sh
Executable file
241
scripts/deploy/deploy-to-server.sh
Executable file
@@ -0,0 +1,241 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Deploy AITBC services to the aitbc server (10.1.223.93)
|
||||
|
||||
set -e
|
||||
|
||||
SERVER="root@10.1.223.93"
|
||||
PROJECT_DIR="/root/aitbc"
|
||||
|
||||
echo "🚀 Deploying AITBC to Server"
|
||||
echo "=========================="
|
||||
echo "Server: $SERVER"
|
||||
echo ""
|
||||
|
||||
# Colors
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
print_status() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
# Test SSH connection
|
||||
print_status "Testing SSH connection..."
|
||||
ssh $SERVER "hostname && ip a show eth0 | grep inet"
|
||||
|
||||
# Copy project to server
|
||||
print_status "Copying project to server..."
|
||||
ssh $SERVER "rm -rf $PROJECT_DIR 2>/dev/null || true"
|
||||
scp -r /home/oib/windsurf/aitbc $SERVER:/root/
|
||||
|
||||
# Setup Python environment
|
||||
print_status "Setting up Python environment..."
|
||||
ssh $SERVER "cd $PROJECT_DIR && python3 -m venv .venv && source .venv/bin/activate && pip install --upgrade pip"
|
||||
|
||||
# Install dependencies
|
||||
print_status "Installing dependencies..."
|
||||
ssh $SERVER "cd $PROJECT_DIR/apps/coordinator-api && source ../../.venv/bin/activate && pip install -e ."
|
||||
ssh $SERVER "cd $PROJECT_DIR/apps/blockchain-node && source ../../.venv/bin/activate && pip install -e ."
|
||||
|
||||
# Create systemd service files
|
||||
print_status "Creating systemd services..."
|
||||
|
||||
# Coordinator API service
|
||||
ssh $SERVER 'cat > /etc/systemd/system/aitbc-coordinator.service << EOF
|
||||
[Unit]
|
||||
Description=AITBC Coordinator API
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=exec
|
||||
User=root
|
||||
WorkingDirectory=/root/aitbc/apps/coordinator-api
|
||||
Environment=PATH=/root/aitbc/.venv/bin
|
||||
ExecStart=/root/aitbc/.venv/bin/python -m uvicorn src.app.main:app --host 0.0.0.0 --port 8000
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF'
|
||||
|
||||
# Blockchain Node service
|
||||
ssh $SERVER 'cat > /etc/systemd/system/aitbc-blockchain.service << EOF
|
||||
[Unit]
|
||||
Description=AITBC Blockchain Node
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=exec
|
||||
User=root
|
||||
WorkingDirectory=/root/aitbc/apps/blockchain-node
|
||||
Environment=PATH=/root/aitbc/.venv/bin
|
||||
ExecStart=/root/aitbc/.venv/bin/python -m uvicorn aitbc_chain.app:app --host 0.0.0.0 --port 9080
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF'
|
||||
|
||||
# Marketplace UI service
|
||||
ssh $SERVER 'cat > /etc/systemd/system/aitbc-marketplace.service << EOF
|
||||
[Unit]
|
||||
Description=AITBC Marketplace UI
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=exec
|
||||
User=root
|
||||
WorkingDirectory=/root/aitbc/apps/marketplace-ui
|
||||
Environment=PATH=/root/aitbc/.venv/bin
|
||||
ExecStart=/root/aitbc/.venv/bin/python server.py --port 3001
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF'
|
||||
|
||||
# Trade Exchange service
|
||||
ssh $SERVER 'cat > /etc/systemd/system/aitbc-exchange.service << EOF
|
||||
[Unit]
|
||||
Description=AITBC Trade Exchange
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=exec
|
||||
User=root
|
||||
WorkingDirectory=/root/aitbc/apps/trade-exchange
|
||||
Environment=PATH=/root/aitbc/.venv/bin
|
||||
ExecStart=/root/aitbc/.venv/bin/python server.py --port 3002
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF'
|
||||
|
||||
# Install nginx if not installed
|
||||
print_status "Installing nginx..."
|
||||
ssh $SERVER "apt update && apt install -y nginx"
|
||||
|
||||
# Create nginx configuration
|
||||
print_status "Configuring nginx..."
|
||||
ssh $SERVER 'cat > /etc/nginx/sites-available/aitbc << EOF
|
||||
server {
|
||||
listen 80;
|
||||
server_name aitbc.bubuit.net;
|
||||
|
||||
# API routes
|
||||
location /api/ {
|
||||
proxy_pass http://127.0.0.1:8000/v1/;
|
||||
proxy_set_header Host \$host;
|
||||
proxy_set_header X-Real-IP \$remote_addr;
|
||||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto \$scheme;
|
||||
}
|
||||
|
||||
# Admin routes
|
||||
location /admin/ {
|
||||
proxy_pass http://127.0.0.1:8000/admin/;
|
||||
proxy_set_header Host \$host;
|
||||
proxy_set_header X-Real-IP \$remote_addr;
|
||||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto \$scheme;
|
||||
}
|
||||
|
||||
# Blockchain RPC
|
||||
location /rpc/ {
|
||||
proxy_pass http://127.0.0.1:9080/rpc/;
|
||||
proxy_set_header Host \$host;
|
||||
proxy_set_header X-Real-IP \$remote_addr;
|
||||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto \$scheme;
|
||||
}
|
||||
|
||||
# Marketplace UI
|
||||
location /Marketplace {
|
||||
proxy_pass http://127.0.0.1:3001/;
|
||||
proxy_set_header Host \$host;
|
||||
proxy_set_header X-Real-IP \$remote_addr;
|
||||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto \$scheme;
|
||||
}
|
||||
|
||||
# Trade Exchange
|
||||
location /Exchange {
|
||||
proxy_pass http://127.0.0.1:3002/;
|
||||
proxy_set_header Host \$host;
|
||||
proxy_set_header X-Real-IP \$remote_addr;
|
||||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto \$scheme;
|
||||
}
|
||||
|
||||
# Health endpoint
|
||||
location /health {
|
||||
proxy_pass http://127.0.0.1:8000/v1/health;
|
||||
proxy_set_header Host \$host;
|
||||
}
|
||||
|
||||
# Default redirect
|
||||
location / {
|
||||
return 301 /Marketplace;
|
||||
}
|
||||
}
|
||||
EOF'
|
||||
|
||||
# Enable nginx site
|
||||
ssh $SERVER "ln -sf /etc/nginx/sites-available/aitbc /etc/nginx/sites-enabled/"
|
||||
ssh $SERVER "rm -f /etc/nginx/sites-enabled/default"
|
||||
|
||||
# Test and reload nginx
|
||||
ssh $SERVER "nginx -t && systemctl reload nginx"
|
||||
|
||||
# Start services
|
||||
print_status "Starting AITBC services..."
|
||||
ssh $SERVER "systemctl daemon-reload"
|
||||
ssh $SERVER "systemctl enable aitbc-coordinator aitbc-blockchain aitbc-marketplace aitbc-exchange"
|
||||
ssh $SERVER "systemctl start aitbc-coordinator aitbc-blockchain aitbc-marketplace aitbc-exchange"
|
||||
|
||||
# Wait for services to start
|
||||
print_status "Waiting for services to start..."
|
||||
sleep 10
|
||||
|
||||
# Check service status
|
||||
print_status "Checking service status..."
|
||||
ssh $SERVER "systemctl status aitbc-coordinator --no-pager -l | head -10"
|
||||
ssh $SERVER "systemctl status aitbc-blockchain --no-pager -l | head -10"
|
||||
|
||||
# Test endpoints
|
||||
print_status "Testing endpoints..."
|
||||
ssh $SERVER "curl -s http://127.0.0.1:8000/v1/health | head -c 100"
|
||||
echo ""
|
||||
ssh $SERVER "curl -s http://127.0.0.1:8000/v1/admin/stats -H 'X-Api-Key: REDACTED_ADMIN_KEY' | head -c 100"
|
||||
echo ""
|
||||
|
||||
echo ""
|
||||
print_status "✅ Deployment complete!"
|
||||
echo ""
|
||||
echo "📋 Service URLs:"
|
||||
echo " 🌐 Server IP: 10.1.223.93"
|
||||
echo " 📊 Marketplace: http://10.1.223.93/Marketplace"
|
||||
echo " 💱 Trade Exchange: http://10.1.223.93/Exchange"
|
||||
echo " 🔗 API: http://10.1.223.93/api"
|
||||
echo " ⛓️ Blockchain RPC: http://10.1.223.93/rpc"
|
||||
echo ""
|
||||
echo "🔒 Domain URLs (with SSL):"
|
||||
echo " 📊 Marketplace: https://aitbc.bubuit.net/Marketplace"
|
||||
echo " 💱 Trade Exchange: https://aitbc.bubuit.net/Exchange"
|
||||
echo " 🔗 API: https://aitbc.bubuit.net/api"
|
||||
echo " ⛓️ Blockchain RPC: https://aitbc.bubuit.net/rpc"
|
||||
echo ""
|
||||
print_status "To manage services:"
|
||||
echo " ssh aitbc 'systemctl status aitbc-coordinator'"
|
||||
echo " ssh aitbc 'journalctl -u aitbc-coordinator -f'"
|
||||
158
scripts/deploy/deploy_container_with_miner.py
Normal file
158
scripts/deploy/deploy_container_with_miner.py
Normal file
@@ -0,0 +1,158 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Deploy AITBC services to incus container with GPU miner integration
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import time
|
||||
import sys
|
||||
|
||||
def run_command(cmd, container=None):
|
||||
"""Run command locally or in container"""
|
||||
if container:
|
||||
cmd = f"incus exec {container} -- {cmd}"
|
||||
print(f"Running: {cmd}")
|
||||
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
|
||||
if result.returncode != 0:
|
||||
print(f"Error: {result.stderr}")
|
||||
return False
|
||||
return True
|
||||
|
||||
def deploy_to_container():
|
||||
container = "aitbc"
|
||||
container_ip = "10.1.223.93"
|
||||
|
||||
print("🚀 Deploying AITBC services to container with GPU miner...")
|
||||
|
||||
# Check if container exists
|
||||
result = subprocess.run("incus list -c n", shell=True, capture_output=True, text=True)
|
||||
if container not in result.stdout:
|
||||
print(f"\n📦 Creating container {container}...")
|
||||
subprocess.run(f"incus launch images:ubuntu/22.04 {container}", shell=True)
|
||||
time.sleep(10)
|
||||
|
||||
# Ensure container is running
|
||||
subprocess.run(f"incus start {container}", shell=True)
|
||||
time.sleep(5)
|
||||
|
||||
# Update and install packages in container
|
||||
print("\n📦 Installing packages in container...")
|
||||
run_command("apt-get update", container)
|
||||
run_command("apt-get install -y python3 python3-pip python3-venv curl", container)
|
||||
|
||||
# Stop local services
|
||||
print("\n📋 Stopping local services...")
|
||||
subprocess.run("sudo fuser -k 8000/tcp 2>/dev/null || true", shell=True)
|
||||
subprocess.run("sudo fuser -k 9080/tcp 2>/dev/null || true", shell=True)
|
||||
subprocess.run("pkill -f 'marketplace-ui' 2>/dev/null || true", shell=True)
|
||||
subprocess.run("pkill -f 'trade-exchange' 2>/dev/null || true", shell=True)
|
||||
|
||||
# Copy project to container
|
||||
print("\n📁 Copying project to container...")
|
||||
subprocess.run(f"incus file push -r /home/oib/windsurf/aitbc {container}/home/oib/", shell=True)
|
||||
|
||||
# Setup Python environment in container
|
||||
print("\n🐍 Setting up Python environment...")
|
||||
run_command("cd /home/oib/aitbc && python3 -m venv .venv", container)
|
||||
run_command("cd /home/oib/aitbc && source .venv/bin/activate && pip install fastapi uvicorn httpx sqlmodel psutil", container)
|
||||
|
||||
# Install dependencies
|
||||
print("\n📦 Installing dependencies...")
|
||||
run_command("cd /home/oib/aitbc/apps/coordinator-api && source ../../.venv/bin/activate && pip install -e .", container)
|
||||
run_command("cd /home/oib/aitbc/apps/blockchain-node && source ../../.venv/bin/activate && pip install -e .", container)
|
||||
|
||||
# Create startup script with GPU miner
|
||||
print("\n🔧 Creating startup script with GPU miner...")
|
||||
startup_script = """#!/bin/bash
|
||||
cd /home/oib/aitbc
|
||||
source .venv/bin/activate
|
||||
|
||||
# Start coordinator API
|
||||
echo "Starting Coordinator API..."
|
||||
cd apps/coordinator-api
|
||||
source ../../.venv/bin/activate
|
||||
python -m uvicorn app.main:app --host 0.0.0.0 --port 8000 &
|
||||
COORD_PID=$!
|
||||
|
||||
# Start blockchain node
|
||||
echo "Starting Blockchain Node..."
|
||||
cd ../../apps/blockchain-node
|
||||
source ../../.venv/bin/activate
|
||||
python -m uvicorn aitbc_chain.app:app --host 0.0.0.0 --port 9080 &
|
||||
BLOCK_PID=$!
|
||||
|
||||
# Start trade exchange
|
||||
echo "Starting Trade Exchange..."
|
||||
cd ../../apps/trade-exchange
|
||||
source ../../.venv/bin/activate
|
||||
python simple_exchange_api.py &
|
||||
EXCHANGE_PID=$!
|
||||
|
||||
# Start GPU registry
|
||||
echo "Starting GPU Registry..."
|
||||
cd ../..
|
||||
python gpu_registry_demo.py &
|
||||
REGISTRY_PID=$!
|
||||
|
||||
# Start GPU miner
|
||||
echo "Starting GPU Miner..."
|
||||
python gpu_miner_with_wait.py &
|
||||
MINER_PID=$!
|
||||
|
||||
echo "All services started!"
|
||||
echo "Coordinator API: http://10.1.223.93:8000"
|
||||
echo "Blockchain RPC: http://10.1.223.93:9080"
|
||||
echo "Trade Exchange: http://10.1.223.93:3002"
|
||||
echo "GPU Registry: http://10.1.223.93:8091"
|
||||
|
||||
# Wait for services
|
||||
wait $COORD_PID $BLOCK_PID $EXCHANGE_PID $REGISTRY_PID $MINER_PID
|
||||
"""
|
||||
|
||||
# Write startup script to container
|
||||
with open('/tmp/startup.sh', 'w') as f:
|
||||
f.write(startup_script)
|
||||
subprocess.run(f"incus file push /tmp/startup.sh {container}/home/oib/aitbc/", shell=True)
|
||||
run_command("chmod +x /home/oib/aitbc/startup.sh", container)
|
||||
|
||||
# Create systemd service
|
||||
print("\n⚙️ Creating systemd service...")
|
||||
service_content = """[Unit]
|
||||
Description=AITBC Services with GPU Miner
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=oib
|
||||
WorkingDirectory=/home/oib/aitbc
|
||||
ExecStart=/home/oib/aitbc/startup.sh
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
"""
|
||||
|
||||
with open('/tmp/aitbc.service', 'w') as f:
|
||||
f.write(service_content)
|
||||
subprocess.run(f"incus file push /tmp/aitbc.service {container}/tmp/", shell=True)
|
||||
run_command("mv /tmp/aitbc.service /etc/systemd/system/", container)
|
||||
run_command("systemctl daemon-reload", container)
|
||||
run_command("systemctl enable aitbc.service", container)
|
||||
run_command("systemctl start aitbc.service", container)
|
||||
|
||||
print("\n✅ Deployment complete!")
|
||||
print(f"\n📊 Service URLs:")
|
||||
print(f" - Coordinator API: http://{container_ip}:8000")
|
||||
print(f" - Blockchain RPC: http://{container_ip}:9080")
|
||||
print(f" - Trade Exchange: http://{container_ip}:3002")
|
||||
print(f" - GPU Registry: http://{container_ip}:8091")
|
||||
print(f"\n🔍 Check GPU status:")
|
||||
print(f" curl http://{container_ip}:8091/miners/list")
|
||||
|
||||
print(f"\n📋 To manage services in container:")
|
||||
print(f" incus exec {container} -- systemctl status aitbc")
|
||||
print(f" incus exec {container} -- journalctl -u aitbc -f")
|
||||
|
||||
if __name__ == "__main__":
|
||||
deploy_to_container()
|
||||
130
scripts/deploy/deploy_gpu_to_container.py
Normal file
130
scripts/deploy/deploy_gpu_to_container.py
Normal file
@@ -0,0 +1,130 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Deploy GPU Miner Integration to AITBC Container
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
def run_in_container(cmd):
|
||||
"""Run command in aitbc container"""
|
||||
full_cmd = f"incus exec aitbc -- {cmd}"
|
||||
print(f"Running: {full_cmd}")
|
||||
result = subprocess.run(full_cmd, shell=True, capture_output=True, text=True)
|
||||
if result.returncode != 0:
|
||||
print(f"Error: {result.stderr}")
|
||||
return False, result.stderr
|
||||
return True, result.stdout
|
||||
|
||||
def deploy_gpu_miner_to_container():
|
||||
print("🚀 Deploying GPU Miner Integration to AITBC Container...")
|
||||
|
||||
# Check container access
|
||||
print("\n1. 🔍 Checking container access...")
|
||||
success, output = run_in_container("whoami")
|
||||
if success:
|
||||
print(f" Container user: {output.strip()}")
|
||||
else:
|
||||
print(" ❌ Cannot access container")
|
||||
return
|
||||
|
||||
# Copy GPU miner files to container
|
||||
print("\n2. 📁 Copying GPU miner files...")
|
||||
files_to_copy = [
|
||||
"gpu_miner_with_wait.py",
|
||||
"gpu_registry_demo.py"
|
||||
]
|
||||
|
||||
for file in files_to_copy:
|
||||
cmd = f"incus file push /home/oib/windsurf/aitbc/{file} aitbc/home/oib/"
|
||||
print(f" Copying {file}...")
|
||||
result = subprocess.run(cmd, shell=True)
|
||||
if result.returncode == 0:
|
||||
print(f" ✅ {file} copied")
|
||||
else:
|
||||
print(f" ❌ Failed to copy {file}")
|
||||
|
||||
# Install dependencies in container
|
||||
print("\n3. 📦 Installing dependencies...")
|
||||
run_in_container("pip install httpx fastapi uvicorn psutil")
|
||||
|
||||
# Create GPU miner service in container
|
||||
print("\n4. ⚙️ Creating GPU miner service...")
|
||||
service_content = """[Unit]
|
||||
Description=AITBC GPU Miner Client
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=oib
|
||||
WorkingDirectory=/home/oib
|
||||
ExecStart=/usr/bin/python3 gpu_miner_with_wait.py
|
||||
Restart=always
|
||||
RestartSec=30
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
"""
|
||||
|
||||
# Write service file to container
|
||||
with open('/tmp/gpu-miner.service', 'w') as f:
|
||||
f.write(service_content)
|
||||
subprocess.run("incus file push /tmp/gpu-miner.service aitbc/tmp/", shell=True)
|
||||
run_in_container("sudo mv /tmp/gpu-miner.service /etc/systemd/system/")
|
||||
run_in_container("sudo systemctl daemon-reload")
|
||||
run_in_container("sudo systemctl enable gpu-miner.service")
|
||||
run_in_container("sudo systemctl start gpu-miner.service")
|
||||
|
||||
# Create GPU registry service in container
|
||||
print("\n5. 🎮 Creating GPU registry service...")
|
||||
registry_service = """[Unit]
|
||||
Description=AITBC GPU Registry
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=oib
|
||||
WorkingDirectory=/home/oib
|
||||
ExecStart=/usr/bin/python3 gpu_registry_demo.py
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
"""
|
||||
|
||||
with open('/tmp/gpu-registry.service', 'w') as f:
|
||||
f.write(registry_service)
|
||||
subprocess.run("incus file push /tmp/gpu-registry.service aitbc/tmp/", shell=True)
|
||||
run_in_container("sudo mv /tmp/gpu-registry.service /etc/systemd/system/")
|
||||
run_in_container("sudo systemctl daemon-reload")
|
||||
run_in_container("sudo systemctl enable gpu-registry.service")
|
||||
run_in_container("sudo systemctl start gpu-registry.service")
|
||||
|
||||
# Check services
|
||||
print("\n6. 📊 Checking services...")
|
||||
success, output = run_in_container("sudo systemctl status gpu-miner.service --no-pager")
|
||||
print(output)
|
||||
|
||||
success, output = run_in_container("sudo systemctl status gpu-registry.service --no-pager")
|
||||
print(output)
|
||||
|
||||
# Update coordinator to include miner endpoints
|
||||
print("\n7. 🔗 Updating coordinator API...")
|
||||
|
||||
print("\n✅ GPU Miner deployed to container!")
|
||||
print("\n📊 Access URLs:")
|
||||
print(" - Container IP: 10.1.223.93")
|
||||
print(" - GPU Registry: http://10.1.223.93:8091/miners/list")
|
||||
print(" - Coordinator API: http://10.1.223.93:8000")
|
||||
|
||||
print("\n🔧 To manage services in container:")
|
||||
print(" incus exec aitbc -- sudo systemctl status gpu-miner")
|
||||
print(" incus exec aitbc -- sudo journalctl -u gpu-miner -f")
|
||||
|
||||
if __name__ == "__main__":
|
||||
deploy_gpu_miner_to_container()
|
||||
177
scripts/dev_services.sh
Executable file
177
scripts/dev_services.sh
Executable file
@@ -0,0 +1,177 @@
|
||||
#!/bin/bash
|
||||
|
||||
# AITBC Development Services Manager
|
||||
# Starts AITBC services for development and provides cleanup option
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||
LOG_DIR="$PROJECT_ROOT/logs"
|
||||
PID_FILE="$PROJECT_ROOT/.aitbc_dev_pids"
|
||||
|
||||
# Create logs directory if it doesn't exist
|
||||
mkdir -p "$LOG_DIR"
|
||||
|
||||
# Color codes for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Services to manage
|
||||
SERVICES=(
|
||||
"aitbc-blockchain-node.service"
|
||||
"aitbc-blockchain-rpc.service"
|
||||
"aitbc-gpu-miner.service"
|
||||
"aitbc-mock-coordinator.service"
|
||||
)
|
||||
|
||||
start_services() {
|
||||
echo -e "${BLUE}Starting AITBC development services...${NC}"
|
||||
|
||||
# Check if services are already running
|
||||
for service in "${SERVICES[@]}"; do
|
||||
if systemctl is-active --quiet "$service"; then
|
||||
echo -e "${YELLOW}Warning: $service is already running${NC}"
|
||||
fi
|
||||
done
|
||||
|
||||
# Start all services
|
||||
for service in "${SERVICES[@]}"; do
|
||||
echo -e "Starting $service..."
|
||||
sudo systemctl start "$service"
|
||||
|
||||
# Wait a moment and check if it started successfully
|
||||
sleep 2
|
||||
if systemctl is-active --quiet "$service"; then
|
||||
echo -e "${GREEN}✓ $service started successfully${NC}"
|
||||
echo "$service" >> "$PID_FILE"
|
||||
else
|
||||
echo -e "${RED}✗ Failed to start $service${NC}"
|
||||
echo -e "${RED}Check logs: sudo journalctl -u $service${NC}"
|
||||
fi
|
||||
done
|
||||
|
||||
echo -e "\n${GREEN}AITBC services started!${NC}"
|
||||
echo -e "Use '$0 stop' to stop all services"
|
||||
echo -e "Use '$0 status' to check service status"
|
||||
}
|
||||
|
||||
stop_services() {
|
||||
echo -e "${BLUE}Stopping AITBC development services...${NC}"
|
||||
|
||||
for service in "${SERVICES[@]}"; do
|
||||
if systemctl is-active --quiet "$service"; then
|
||||
echo -e "Stopping $service..."
|
||||
sudo systemctl stop "$service"
|
||||
echo -e "${GREEN}✓ $service stopped${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}$service was not running${NC}"
|
||||
fi
|
||||
done
|
||||
|
||||
# Clean up PID file
|
||||
rm -f "$PID_FILE"
|
||||
echo -e "\n${GREEN}All AITBC services stopped${NC}"
|
||||
}
|
||||
|
||||
show_status() {
|
||||
echo -e "${BLUE}AITBC Service Status:${NC}\n"
|
||||
|
||||
for service in "${SERVICES[@]}"; do
|
||||
if systemctl is-active --quiet "$service"; then
|
||||
echo -e "${GREEN}● $service: RUNNING${NC}"
|
||||
# Show uptime
|
||||
uptime=$(systemctl show "$service" --property=ActiveEnterTimestamp --value)
|
||||
echo -e " Running since: $uptime"
|
||||
else
|
||||
echo -e "${RED}● $service: STOPPED${NC}"
|
||||
fi
|
||||
done
|
||||
|
||||
# Show recent logs if any services are running
|
||||
echo -e "\n${BLUE}Recent logs (last 10 lines each):${NC}"
|
||||
for service in "${SERVICES[@]}"; do
|
||||
if systemctl is-active --quiet "$service"; then
|
||||
echo -e "\n${YELLOW}--- $service ---${NC}"
|
||||
sudo journalctl -u "$service" -n 5 --no-pager | tail -n 5
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
show_logs() {
|
||||
local service="$1"
|
||||
if [ -z "$service" ]; then
|
||||
echo -e "${BLUE}Following logs for all AITBC services...${NC}"
|
||||
sudo journalctl -f -u aitbc-blockchain-node.service -u aitbc-blockchain-rpc.service -u aitbc-gpu-miner.service -u aitbc-mock-coordinator.service
|
||||
else
|
||||
echo -e "${BLUE}Following logs for $service...${NC}"
|
||||
sudo journalctl -f -u "$service"
|
||||
fi
|
||||
}
|
||||
|
||||
restart_services() {
|
||||
echo -e "${BLUE}Restarting AITBC services...${NC}"
|
||||
stop_services
|
||||
sleep 3
|
||||
start_services
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
echo -e "${BLUE}Performing cleanup...${NC}"
|
||||
stop_services
|
||||
|
||||
# Additional cleanup
|
||||
echo -e "Cleaning up temporary files..."
|
||||
rm -f "$PROJECT_ROOT/.aitbc_dev_pids"
|
||||
|
||||
# Clear any lingering processes (optional)
|
||||
echo -e "Checking for lingering processes..."
|
||||
pkill -f "aitbc" || echo "No lingering processes found"
|
||||
|
||||
echo -e "${GREEN}Cleanup complete${NC}"
|
||||
}
|
||||
|
||||
# Handle script interruption for Ctrl+C only
|
||||
trap cleanup INT
|
||||
|
||||
# Main script logic
|
||||
case "$1" in
|
||||
start)
|
||||
start_services
|
||||
;;
|
||||
stop)
|
||||
stop_services
|
||||
;;
|
||||
restart)
|
||||
restart_services
|
||||
;;
|
||||
status)
|
||||
show_status
|
||||
;;
|
||||
logs)
|
||||
show_logs "$2"
|
||||
;;
|
||||
cleanup)
|
||||
cleanup
|
||||
;;
|
||||
*)
|
||||
echo -e "${BLUE}AITBC Development Services Manager${NC}"
|
||||
echo -e "\nUsage: $0 {start|stop|restart|status|logs|cleanup}"
|
||||
echo -e "\nCommands:"
|
||||
echo -e " start - Start all AITBC services"
|
||||
echo -e " stop - Stop all AITBC services"
|
||||
echo -e " restart - Restart all AITBC services"
|
||||
echo -e " status - Show service status"
|
||||
echo -e " logs - Follow logs (optional: specify service name)"
|
||||
echo -e " cleanup - Stop services and clean up"
|
||||
echo -e "\nExamples:"
|
||||
echo -e " $0 start # Start all services"
|
||||
echo -e " $0 logs # Follow all logs"
|
||||
echo -e " $0 logs node # Follow node logs only"
|
||||
echo -e " $0 stop # Stop all services"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
151
scripts/exchange-router-fixed.py
Normal file
151
scripts/exchange-router-fixed.py
Normal file
@@ -0,0 +1,151 @@
|
||||
"""
|
||||
Bitcoin Exchange Router for AITBC
|
||||
"""
|
||||
|
||||
from typing import Dict, Any
|
||||
from fastapi import APIRouter, HTTPException, BackgroundTasks
|
||||
from sqlmodel import Session
|
||||
import uuid
|
||||
import time
|
||||
import json
|
||||
import os
|
||||
|
||||
from ..deps import require_admin_key, require_client_key
|
||||
from ..domain import Wallet
|
||||
from ..schemas import ExchangePaymentRequest, ExchangePaymentResponse
|
||||
|
||||
router = APIRouter(tags=["exchange"])
|
||||
|
||||
# In-memory storage for demo (use database in production)
|
||||
payments: Dict[str, Dict] = {}
|
||||
|
||||
# Bitcoin configuration
|
||||
BITCOIN_CONFIG = {
|
||||
'testnet': True,
|
||||
'main_address': 'tb1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh', # Testnet address
|
||||
'exchange_rate': 100000, # 1 BTC = 100,000 AITBC
|
||||
'min_confirmations': 1,
|
||||
'payment_timeout': 3600 # 1 hour
|
||||
}
|
||||
|
||||
@router.post("/exchange/create-payment", response_model=ExchangePaymentResponse)
|
||||
async def create_payment(
|
||||
request: ExchangePaymentRequest,
|
||||
background_tasks: BackgroundTasks,
|
||||
api_key: str = require_client_key()
|
||||
) -> Dict[str, Any]:
|
||||
"""Create a new Bitcoin payment request"""
|
||||
|
||||
# Validate request
|
||||
if request.aitbc_amount <= 0 or request.btc_amount <= 0:
|
||||
raise HTTPException(status_code=400, detail="Invalid amount")
|
||||
|
||||
# Calculate expected BTC amount
|
||||
expected_btc = request.aitbc_amount / BITCOIN_CONFIG['exchange_rate']
|
||||
|
||||
# Allow small difference for rounding
|
||||
if abs(request.btc_amount - expected_btc) > 0.00000001:
|
||||
raise HTTPException(status_code=400, detail="Amount mismatch")
|
||||
|
||||
# Create payment record
|
||||
payment_id = str(uuid.uuid4())
|
||||
payment = {
|
||||
'payment_id': payment_id,
|
||||
'user_id': request.user_id,
|
||||
'aitbc_amount': request.aitbc_amount,
|
||||
'btc_amount': request.btc_amount,
|
||||
'payment_address': BITCOIN_CONFIG['main_address'],
|
||||
'status': 'pending',
|
||||
'created_at': int(time.time()),
|
||||
'expires_at': int(time.time()) + BITCOIN_CONFIG['payment_timeout'],
|
||||
'confirmations': 0,
|
||||
'tx_hash': None
|
||||
}
|
||||
|
||||
# Store payment
|
||||
payments[payment_id] = payment
|
||||
|
||||
# Start payment monitoring in background
|
||||
background_tasks.add_task(monitor_payment, payment_id)
|
||||
|
||||
return payment
|
||||
|
||||
@router.get("/exchange/payment-status/{payment_id}")
|
||||
async def get_payment_status(payment_id: str) -> Dict[str, Any]:
|
||||
"""Get payment status"""
|
||||
|
||||
if payment_id not in payments:
|
||||
raise HTTPException(status_code=404, detail="Payment not found")
|
||||
|
||||
payment = payments[payment_id]
|
||||
|
||||
# Check if expired
|
||||
if payment['status'] == 'pending' and time.time() > payment['expires_at']:
|
||||
payment['status'] = 'expired'
|
||||
|
||||
return payment
|
||||
|
||||
@router.post("/exchange/confirm-payment/{payment_id}")
|
||||
async def confirm_payment(
|
||||
payment_id: str,
|
||||
tx_hash: str,
|
||||
api_key: str = require_admin_key()
|
||||
) -> Dict[str, Any]:
|
||||
"""Confirm payment (webhook from payment processor)"""
|
||||
|
||||
if payment_id not in payments:
|
||||
raise HTTPException(status_code=404, detail="Payment not found")
|
||||
|
||||
payment = payments[payment_id]
|
||||
|
||||
if payment['status'] != 'pending':
|
||||
raise HTTPException(status_code=400, detail="Payment not in pending state")
|
||||
|
||||
# Verify transaction (in production, verify with blockchain API)
|
||||
# For demo, we'll accept any tx_hash
|
||||
|
||||
payment['status'] = 'confirmed'
|
||||
payment['tx_hash'] = tx_hash
|
||||
payment['confirmed_at'] = int(time.time())
|
||||
|
||||
# Mint AITBC tokens to user's wallet
|
||||
try:
|
||||
from ..services.blockchain import mint_tokens
|
||||
await mint_tokens(payment['user_id'], payment['aitbc_amount'])
|
||||
except Exception as e:
|
||||
print(f"Error minting tokens: {e}")
|
||||
# In production, handle this error properly
|
||||
|
||||
return {
|
||||
'status': 'ok',
|
||||
'payment_id': payment_id,
|
||||
'aitbc_amount': payment['aitbc_amount']
|
||||
}
|
||||
|
||||
@router.get("/exchange/rates")
|
||||
async def get_exchange_rates() -> Dict[str, float]:
|
||||
"""Get current exchange rates"""
|
||||
|
||||
return {
|
||||
'btc_to_aitbc': BITCOIN_CONFIG['exchange_rate'],
|
||||
'aitbc_to_btc': 1.0 / BITCOIN_CONFIG['exchange_rate'],
|
||||
'fee_percent': 0.5
|
||||
}
|
||||
|
||||
async def monitor_payment(payment_id: str):
|
||||
"""Monitor payment for confirmation (background task)"""
|
||||
|
||||
import asyncio
|
||||
|
||||
while payment_id in payments:
|
||||
payment = payments[payment_id]
|
||||
|
||||
# Check if expired
|
||||
if payment['status'] == 'pending' and time.time() > payment['expires_at']:
|
||||
payment['status'] = 'expired'
|
||||
break
|
||||
|
||||
# In production, check blockchain for payment
|
||||
# For demo, we'll wait for manual confirmation
|
||||
|
||||
await asyncio.sleep(30) # Check every 30 seconds
|
||||
89
scripts/gpu/deploy_gpu_all_in_one.sh
Normal file
89
scripts/gpu/deploy_gpu_all_in_one.sh
Normal file
@@ -0,0 +1,89 @@
|
||||
#!/bin/bash
|
||||
# Deploy GPU Miner to AITBC Container - All in One
|
||||
|
||||
set -e
|
||||
|
||||
echo "🚀 Deploying GPU Miner to AITBC Container..."
|
||||
|
||||
# Step 1: Copy files
|
||||
echo "1. Copying GPU scripts..."
|
||||
scp -o StrictHostKeyChecking=no /home/oib/windsurf/aitbc/gpu_registry_demo.py aitbc:/home/oib/
|
||||
scp -o StrictHostKeyChecking=no /home/oib/windsurf/aitbc/gpu_miner_with_wait.py aitbc:/home/oib/
|
||||
|
||||
# Step 2: Install Python and deps
|
||||
echo "2. Installing Python and dependencies..."
|
||||
ssh aitbc 'sudo apt-get update -qq'
|
||||
ssh aitbc 'sudo apt-get install -y -qq python3 python3-venv python3-pip'
|
||||
ssh aitbc 'python3 -m venv /home/oib/.venv-gpu'
|
||||
ssh aitbc '/home/oib/.venv-gpu/bin/pip install -q fastapi uvicorn httpx psutil'
|
||||
|
||||
# Step 3: Create GPU registry service
|
||||
echo "3. Creating GPU registry service..."
|
||||
ssh aitbc "sudo tee /etc/systemd/system/aitbc-gpu-registry.service >/dev/null <<'EOF'
|
||||
[Unit]
|
||||
Description=AITBC GPU Registry
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=oib
|
||||
WorkingDirectory=/home/oib
|
||||
ExecStart=/home/oib/.venv-gpu/bin/python /home/oib/gpu_registry_demo.py
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF"
|
||||
|
||||
# Step 4: Start GPU registry
|
||||
echo "4. Starting GPU registry..."
|
||||
ssh aitbc 'sudo systemctl daemon-reload'
|
||||
ssh aitbc 'sudo systemctl enable --now aitbc-gpu-registry.service'
|
||||
|
||||
# Step 5: Create GPU miner service
|
||||
echo "5. Creating GPU miner service..."
|
||||
ssh aitbc "sudo tee /etc/systemd/system/aitbc-gpu-miner.service >/dev/null <<'EOF'
|
||||
[Unit]
|
||||
Description=AITBC GPU Miner Client
|
||||
After=network.target aitbc-gpu-registry.service
|
||||
Wants=aitbc-gpu-registry.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=oib
|
||||
WorkingDirectory=/home/oib
|
||||
ExecStart=/home/oib/.venv-gpu/bin/python /home/oib/gpu_miner_with_wait.py
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF"
|
||||
|
||||
# Step 6: Start GPU miner
|
||||
echo "6. Starting GPU miner..."
|
||||
ssh aitbc 'sudo systemctl daemon-reload'
|
||||
ssh aitbc 'sudo systemctl enable --now aitbc-gpu-miner.service'
|
||||
|
||||
# Step 7: Check services
|
||||
echo "7. Checking services..."
|
||||
echo -e "\n=== GPU Registry Service ==="
|
||||
ssh aitbc 'sudo systemctl status aitbc-gpu-registry.service --no-pager'
|
||||
|
||||
echo -e "\n=== GPU Miner Service ==="
|
||||
ssh aitbc 'sudo systemctl status aitbc-gpu-miner.service --no-pager'
|
||||
|
||||
# Step 8: Verify GPU registration
|
||||
echo -e "\n8. Verifying GPU registration..."
|
||||
sleep 3
|
||||
echo " curl http://10.1.223.93:8091/miners/list"
|
||||
curl -s http://10.1.223.93:8091/miners/list | python3 -c "import sys,json; data=json.load(sys.stdin); print(f'✅ Found {len(data.get(\"gpus\", []))} GPU(s)'); [print(f' - {gpu[\"capabilities\"][\"gpu\"][\"model\"]} ({gpu[\"capabilities\"][\"gpu\"][\"memory_gb\"]}GB)') for gpu in data.get('gpus', [])]"
|
||||
|
||||
echo -e "\n✅ Deployment complete!"
|
||||
echo "GPU Registry: http://10.1.223.93:8091"
|
||||
echo "GPU Miner: Running and sending heartbeats"
|
||||
89
scripts/gpu/deploy_gpu_container.sh
Normal file
89
scripts/gpu/deploy_gpu_container.sh
Normal file
@@ -0,0 +1,89 @@
|
||||
#!/bin/bash
|
||||
# Deploy GPU Miner to AITBC Container
|
||||
|
||||
echo "🚀 Deploying GPU Miner to AITBC Container..."
|
||||
|
||||
# Check if container is accessible
|
||||
echo "1. Checking container access..."
|
||||
sudo incus exec aitbc -- whoami
|
||||
|
||||
# Copy GPU miner files
|
||||
echo "2. Copying GPU miner files..."
|
||||
sudo incus file push /home/oib/windsurf/aitbc/gpu_miner_with_wait.py aitbc/home/oib/
|
||||
sudo incus file push /home/oib/windsurf/aitbc/gpu_registry_demo.py aitbc/home/oib/
|
||||
|
||||
# Install dependencies
|
||||
echo "3. Installing dependencies..."
|
||||
sudo incus exec aitbc -- pip install httpx fastapi uvicorn psutil
|
||||
|
||||
# Create GPU miner service
|
||||
echo "4. Creating GPU miner service..."
|
||||
cat << 'EOF' | sudo tee /tmp/gpu-miner.service
|
||||
[Unit]
|
||||
Description=AITBC GPU Miner Client
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=oib
|
||||
WorkingDirectory=/home/oib
|
||||
ExecStart=/usr/bin/python3 gpu_miner_with_wait.py
|
||||
Restart=always
|
||||
RestartSec=30
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
sudo incus file push /tmp/gpu-miner.service aitbc/tmp/
|
||||
sudo incus exec aitbc -- sudo mv /tmp/gpu-miner.service /etc/systemd/system/
|
||||
sudo incus exec aitbc -- sudo systemctl daemon-reload
|
||||
sudo incus exec aitbc -- sudo systemctl enable gpu-miner.service
|
||||
sudo incus exec aitbc -- sudo systemctl start gpu-miner.service
|
||||
|
||||
# Create GPU registry service
|
||||
echo "5. Creating GPU registry service..."
|
||||
cat << 'EOF' | sudo tee /tmp/gpu-registry.service
|
||||
[Unit]
|
||||
Description=AITBC GPU Registry
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=oib
|
||||
WorkingDirectory=/home/oib
|
||||
ExecStart=/usr/bin/python3 gpu_registry_demo.py
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
sudo incus file push /tmp/gpu-registry.service aitbc/tmp/
|
||||
sudo incus exec aitbc -- sudo mv /tmp/gpu-registry.service /etc/systemd/system/
|
||||
sudo incus exec aitbc -- sudo systemctl daemon-reload
|
||||
sudo incus exec aitbc -- sudo systemctl enable gpu-registry.service
|
||||
sudo incus exec aitbc -- sudo systemctl start gpu-registry.service
|
||||
|
||||
# Check services
|
||||
echo "6. Checking services..."
|
||||
echo "GPU Miner Service:"
|
||||
sudo incus exec aitbc -- sudo systemctl status gpu-miner.service --no-pager
|
||||
|
||||
echo -e "\nGPU Registry Service:"
|
||||
sudo incus exec aitbc -- sudo systemctl status gpu-registry.service --no-pager
|
||||
|
||||
# Show access URLs
|
||||
echo -e "\n✅ Deployment complete!"
|
||||
echo "Access URLs:"
|
||||
echo " - Container IP: 10.1.223.93"
|
||||
echo " - GPU Registry: http://10.1.223.93:8091/miners/list"
|
||||
echo " - Coordinator API: http://10.1.223.93:8000"
|
||||
|
||||
echo -e "\nTo check GPU status:"
|
||||
echo " curl http://10.1.223.93:8091/miners/list"
|
||||
92
scripts/gpu/gpu_exchange_status.py
Normal file
92
scripts/gpu/gpu_exchange_status.py
Normal file
@@ -0,0 +1,92 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
GPU Exchange Integration Demo
|
||||
Shows how the GPU miner is integrated with the exchange
|
||||
"""
|
||||
|
||||
import json
|
||||
import httpx
|
||||
import subprocess
|
||||
import time
|
||||
from datetime import datetime
|
||||
|
||||
print("🔗 AITBC GPU Exchange Integration")
|
||||
print("=" * 50)
|
||||
|
||||
# Check GPU Registry
|
||||
print("\n1. 📊 Checking GPU Registry...")
|
||||
try:
|
||||
response = httpx.get("http://localhost:8091/miners/list")
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
gpus = data.get("gpus", [])
|
||||
print(f" Found {len(gpus)} registered GPU(s)")
|
||||
|
||||
for gpu in gpus:
|
||||
print(f"\n 🎮 GPU Details:")
|
||||
print(f" Model: {gpu['capabilities']['gpu']['model']}")
|
||||
print(f" Memory: {gpu['capabilities']['gpu']['memory_gb']} GB")
|
||||
print(f" CUDA: {gpu['capabilities']['gpu']['cuda_version']}")
|
||||
print(f" Status: {gpu.get('status', 'Unknown')}")
|
||||
print(f" Region: {gpu.get('region', 'Unknown')}")
|
||||
else:
|
||||
print(" ❌ GPU Registry not accessible")
|
||||
except Exception as e:
|
||||
print(f" ❌ Error: {e}")
|
||||
|
||||
# Check Exchange
|
||||
print("\n2. 💰 Checking Trade Exchange...")
|
||||
try:
|
||||
response = httpx.get("http://localhost:3002")
|
||||
if response.status_code == 200:
|
||||
print(" ✅ Trade Exchange is running")
|
||||
print(" 🌐 URL: http://localhost:3002")
|
||||
else:
|
||||
print(" ❌ Trade Exchange not responding")
|
||||
except:
|
||||
print(" ❌ Trade Exchange not accessible")
|
||||
|
||||
# Check Blockchain
|
||||
print("\n3. ⛓️ Checking Blockchain Node...")
|
||||
try:
|
||||
response = httpx.get("http://localhost:9080/rpc/head")
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
print(f" ✅ Blockchain Node active")
|
||||
print(f" Block Height: {data.get('height', 'Unknown')}")
|
||||
print(f" Block Hash: {data.get('hash', 'Unknown')[:16]}...")
|
||||
else:
|
||||
print(" ❌ Blockchain Node not responding")
|
||||
except:
|
||||
print(" ❌ Blockchain Node not accessible")
|
||||
|
||||
# Show Integration Points
|
||||
print("\n4. 🔌 Integration Points:")
|
||||
print(" • GPU Registry: http://localhost:8091/miners/list")
|
||||
print(" • Trade Exchange: http://localhost:3002")
|
||||
print(" • Blockchain RPC: http://localhost:9080")
|
||||
print(" • GPU Marketplace: Exchange > Browse GPU Marketplace")
|
||||
|
||||
# Show API Usage
|
||||
print("\n5. 📡 API Usage Examples:")
|
||||
print("\n Get registered GPUs:")
|
||||
print(" curl http://localhost:8091/miners/list")
|
||||
print("\n Get GPU details:")
|
||||
print(" curl http://localhost:8091/miners/localhost-gpu-miner")
|
||||
print("\n Get blockchain info:")
|
||||
print(" curl http://localhost:9080/rpc/head")
|
||||
|
||||
# Show Current Status
|
||||
print("\n6. 📈 Current System Status:")
|
||||
print(" ✅ GPU Miner: Running (systemd)")
|
||||
print(" ✅ GPU Registry: Running on port 8091")
|
||||
print(" ✅ Trade Exchange: Running on port 3002")
|
||||
print(" ✅ Blockchain Node: Running on port 9080")
|
||||
|
||||
print("\n" + "=" * 50)
|
||||
print("🎯 GPU is successfully integrated with the exchange!")
|
||||
print("\nNext steps:")
|
||||
print("1. Open http://localhost:3002 in your browser")
|
||||
print("2. Click 'Browse GPU Marketplace'")
|
||||
print("3. View the registered RTX 4060 Ti GPU")
|
||||
print("4. Purchase GPU compute time with AITBC tokens")
|
||||
60
scripts/gpu/gpu_miner_demo.py
Normal file
60
scripts/gpu/gpu_miner_demo.py
Normal file
@@ -0,0 +1,60 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
GPU Miner Registration Demo
|
||||
Shows what data would be sent to register the GPU
|
||||
"""
|
||||
|
||||
import json
|
||||
from datetime import datetime
|
||||
|
||||
# GPU Information from nvidia-smi
|
||||
GPU_INFO = {
|
||||
"miner_id": "localhost-gpu-miner",
|
||||
"capabilities": {
|
||||
"gpu": {
|
||||
"model": "NVIDIA GeForce RTX 4060 Ti",
|
||||
"memory_gb": 16,
|
||||
"cuda_version": "12.4",
|
||||
"compute_capability": "8.9",
|
||||
"driver_version": "550.163.01"
|
||||
},
|
||||
"compute": {
|
||||
"type": "GPU",
|
||||
"platform": "CUDA",
|
||||
"supported_tasks": ["inference", "training", "stable-diffusion", "llama"],
|
||||
"max_concurrent_jobs": 1
|
||||
}
|
||||
},
|
||||
"concurrency": 1,
|
||||
"region": "localhost"
|
||||
}
|
||||
|
||||
print("=== GPU Miner Registration Data ===")
|
||||
print(json.dumps(GPU_INFO, indent=2))
|
||||
print("\n=== Registration Endpoint ===")
|
||||
print("POST http://localhost:8000/miners/register")
|
||||
print("\n=== Headers ===")
|
||||
print("Authorization: Bearer REDACTED_MINER_KEY")
|
||||
print("Content-Type: application/json")
|
||||
print("\n=== Response Expected ===")
|
||||
print("""
|
||||
{
|
||||
"status": "ok",
|
||||
"session_token": "abc123..."
|
||||
}
|
||||
""")
|
||||
|
||||
print("\n=== Current GPU Status ===")
|
||||
print(f"Model: NVIDIA GeForce RTX 4060 Ti")
|
||||
print(f"Memory: 16GB (2682MB/16380MB used)")
|
||||
print(f"Utilization: 9%")
|
||||
print(f"Temperature: 43°C")
|
||||
print(f"Status: Available for mining")
|
||||
|
||||
print("\n=== To Start the GPU Miner ===")
|
||||
print("1. Ensure coordinator API is running on port 8000")
|
||||
print("2. Run: python simple_gpu_miner.py")
|
||||
print("3. The miner will:")
|
||||
print(" - Register GPU capabilities")
|
||||
print(" - Send heartbeats every 15 seconds")
|
||||
print(" - Poll for jobs every 3 seconds")
|
||||
396
scripts/gpu/gpu_miner_host.py
Normal file
396
scripts/gpu/gpu_miner_host.py
Normal file
@@ -0,0 +1,396 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Real GPU Miner Client for AITBC - runs on host with actual GPU
|
||||
"""
|
||||
|
||||
import json
|
||||
import time
|
||||
import httpx
|
||||
import logging
|
||||
import sys
|
||||
import subprocess
|
||||
import os
|
||||
from datetime import datetime
|
||||
|
||||
# Configuration
|
||||
COORDINATOR_URL = "http://127.0.0.1:18000"
|
||||
MINER_ID = "REDACTED_MINER_KEY"
|
||||
AUTH_TOKEN = "REDACTED_MINER_KEY"
|
||||
HEARTBEAT_INTERVAL = 15
|
||||
MAX_RETRIES = 10
|
||||
RETRY_DELAY = 30
|
||||
|
||||
# Setup logging with explicit configuration
|
||||
LOG_PATH = "/home/oib/windsurf/aitbc/logs/host_gpu_miner.log"
|
||||
os.makedirs(os.path.dirname(LOG_PATH), exist_ok=True)
|
||||
|
||||
class FlushHandler(logging.StreamHandler):
|
||||
def emit(self, record):
|
||||
super().emit(record)
|
||||
self.flush()
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(levelname)s - %(message)s',
|
||||
handlers=[
|
||||
FlushHandler(sys.stdout),
|
||||
logging.FileHandler(LOG_PATH)
|
||||
]
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Force stdout to be unbuffered
|
||||
sys.stdout.reconfigure(line_buffering=True)
|
||||
sys.stderr.reconfigure(line_buffering=True)
|
||||
|
||||
# GPU capabilities (RTX 4060 Ti)
|
||||
GPU_CAPABILITIES = {
|
||||
"gpu": {
|
||||
"model": "NVIDIA GeForce RTX 4060 Ti",
|
||||
"memory_gb": 16,
|
||||
"cuda_version": "12.4",
|
||||
"platform": "CUDA",
|
||||
"supported_tasks": ["inference", "training", "stable-diffusion", "llama"],
|
||||
"max_concurrent_jobs": 1
|
||||
}
|
||||
}
|
||||
|
||||
def get_gpu_info():
|
||||
"""Get real GPU information"""
|
||||
try:
|
||||
result = subprocess.run(['nvidia-smi', '--query-gpu=name,memory.total,memory.used,utilization.gpu',
|
||||
'--format=csv,noheader,nounits'],
|
||||
capture_output=True, text=True, timeout=5)
|
||||
if result.returncode == 0:
|
||||
info = result.stdout.strip().split(', ')
|
||||
return {
|
||||
"name": info[0],
|
||||
"memory_total": int(info[1]),
|
||||
"memory_used": int(info[2]),
|
||||
"utilization": int(info[3])
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to get GPU info: {e}")
|
||||
return None
|
||||
|
||||
def check_ollama():
|
||||
"""Check if Ollama is running and has models"""
|
||||
try:
|
||||
response = httpx.get("http://localhost:11434/api/tags", timeout=5)
|
||||
if response.status_code == 200:
|
||||
models = response.json().get('models', [])
|
||||
model_names = [m['name'] for m in models]
|
||||
logger.info(f"Ollama running with models: {model_names}")
|
||||
return True, model_names
|
||||
else:
|
||||
logger.error("Ollama not responding")
|
||||
return False, []
|
||||
except Exception as e:
|
||||
logger.error(f"Ollama check failed: {e}")
|
||||
return False, []
|
||||
|
||||
def wait_for_coordinator():
|
||||
"""Wait for coordinator to be available"""
|
||||
for i in range(MAX_RETRIES):
|
||||
try:
|
||||
response = httpx.get(f"{COORDINATOR_URL}/v1/health", timeout=5)
|
||||
if response.status_code == 200:
|
||||
logger.info("Coordinator is available!")
|
||||
return True
|
||||
except:
|
||||
pass
|
||||
|
||||
logger.info(f"Waiting for coordinator... ({i+1}/{MAX_RETRIES})")
|
||||
time.sleep(RETRY_DELAY)
|
||||
|
||||
logger.error("Coordinator not available after max retries")
|
||||
return False
|
||||
|
||||
def register_miner():
|
||||
"""Register the miner with the coordinator"""
|
||||
register_data = {
|
||||
"capabilities": GPU_CAPABILITIES,
|
||||
"concurrency": 1,
|
||||
"region": "localhost"
|
||||
}
|
||||
|
||||
headers = {
|
||||
"X-Api-Key": AUTH_TOKEN,
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
try:
|
||||
response = httpx.post(
|
||||
f"{COORDINATOR_URL}/v1/miners/register?miner_id={MINER_ID}",
|
||||
json=register_data,
|
||||
headers=headers,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
logger.info(f"Successfully registered miner: {data}")
|
||||
return data.get("session_token", "demo-token")
|
||||
else:
|
||||
logger.error(f"Registration failed: {response.status_code} - {response.text}")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Registration error: {e}")
|
||||
return None
|
||||
|
||||
def send_heartbeat():
|
||||
"""Send heartbeat to coordinator with real GPU stats"""
|
||||
gpu_info = get_gpu_info()
|
||||
|
||||
if gpu_info:
|
||||
heartbeat_data = {
|
||||
"status": "active",
|
||||
"current_jobs": 0,
|
||||
"last_seen": datetime.utcnow().isoformat(),
|
||||
"gpu_utilization": gpu_info["utilization"],
|
||||
"memory_used": gpu_info["memory_used"],
|
||||
"memory_total": gpu_info["memory_total"]
|
||||
}
|
||||
else:
|
||||
heartbeat_data = {
|
||||
"status": "active",
|
||||
"current_jobs": 0,
|
||||
"last_seen": datetime.utcnow().isoformat(),
|
||||
"gpu_utilization": 0,
|
||||
"memory_used": 0,
|
||||
}
|
||||
|
||||
headers = {
|
||||
"X-Api-Key": AUTH_TOKEN,
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
try:
|
||||
response = httpx.post(
|
||||
f"{COORDINATOR_URL}/v1/miners/heartbeat?miner_id={MINER_ID}",
|
||||
json=heartbeat_data,
|
||||
headers=headers,
|
||||
timeout=5
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
logger.info(f"Heartbeat sent (GPU: {gpu_info['utilization'] if gpu_info else 'N/A'}%)")
|
||||
else:
|
||||
logger.error(f"Heartbeat failed: {response.status_code} - {response.text}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Heartbeat error: {e}")
|
||||
|
||||
def execute_job(job, available_models):
|
||||
"""Execute a job using real GPU resources"""
|
||||
job_id = job.get('job_id')
|
||||
payload = job.get('payload', {})
|
||||
|
||||
logger.info(f"Executing job {job_id}: {payload}")
|
||||
|
||||
try:
|
||||
if payload.get('type') == 'inference':
|
||||
# Get the prompt and model
|
||||
prompt = payload.get('prompt', '')
|
||||
model = payload.get('model', 'llama3.2:latest')
|
||||
|
||||
# Check if model is available
|
||||
if model not in available_models:
|
||||
# Use first available model
|
||||
if available_models:
|
||||
model = available_models[0]
|
||||
logger.info(f"Using available model: {model}")
|
||||
else:
|
||||
raise Exception("No models available in Ollama")
|
||||
|
||||
# Call Ollama API for real GPU inference
|
||||
logger.info(f"Running inference on GPU with model: {model}")
|
||||
start_time = time.time()
|
||||
|
||||
ollama_response = httpx.post(
|
||||
"http://localhost:11434/api/generate",
|
||||
json={
|
||||
"model": model,
|
||||
"prompt": prompt,
|
||||
"stream": False
|
||||
},
|
||||
timeout=60
|
||||
)
|
||||
|
||||
if ollama_response.status_code == 200:
|
||||
result = ollama_response.json()
|
||||
output = result.get('response', '')
|
||||
execution_time = time.time() - start_time
|
||||
|
||||
# Get GPU stats after execution
|
||||
gpu_after = get_gpu_info()
|
||||
|
||||
# Submit result back to coordinator
|
||||
submit_result(job_id, {
|
||||
"result": {
|
||||
"status": "completed",
|
||||
"output": output,
|
||||
"model": model,
|
||||
"tokens_processed": result.get('eval_count', 0),
|
||||
"execution_time": execution_time,
|
||||
"gpu_used": True
|
||||
},
|
||||
"metrics": {
|
||||
"gpu_utilization": gpu_after["utilization"] if gpu_after else 0,
|
||||
"memory_used": gpu_after["memory_used"] if gpu_after else 0,
|
||||
"memory_peak": max(gpu_after["memory_used"] if gpu_after else 0, 2048)
|
||||
}
|
||||
})
|
||||
|
||||
logger.info(f"Job {job_id} completed in {execution_time:.2f}s")
|
||||
return True
|
||||
else:
|
||||
logger.error(f"Ollama error: {ollama_response.status_code}")
|
||||
submit_result(job_id, {
|
||||
"result": {
|
||||
"status": "failed",
|
||||
"error": f"Ollama error: {ollama_response.text}"
|
||||
}
|
||||
})
|
||||
return False
|
||||
else:
|
||||
# Unsupported job type
|
||||
logger.error(f"Unsupported job type: {payload.get('type')}")
|
||||
submit_result(job_id, {
|
||||
"result": {
|
||||
"status": "failed",
|
||||
"error": f"Unsupported job type: {payload.get('type')}"
|
||||
}
|
||||
})
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Job execution error: {e}")
|
||||
submit_result(job_id, {
|
||||
"result": {
|
||||
"status": "failed",
|
||||
"error": str(e)
|
||||
}
|
||||
})
|
||||
return False
|
||||
|
||||
def submit_result(job_id, result):
|
||||
"""Submit job result to coordinator"""
|
||||
headers = {
|
||||
"X-Api-Key": AUTH_TOKEN,
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
try:
|
||||
response = httpx.post(
|
||||
f"{COORDINATOR_URL}/v1/miners/{job_id}/result",
|
||||
json=result,
|
||||
headers=headers,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
logger.info(f"Result submitted for job {job_id}")
|
||||
else:
|
||||
logger.error(f"Result submission failed: {response.status_code} - {response.text}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Result submission error: {e}")
|
||||
|
||||
def poll_for_jobs():
|
||||
"""Poll for available jobs"""
|
||||
poll_data = {
|
||||
"max_wait_seconds": 5
|
||||
}
|
||||
|
||||
headers = {
|
||||
"X-Api-Key": AUTH_TOKEN,
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
try:
|
||||
response = httpx.post(
|
||||
f"{COORDINATOR_URL}/v1/miners/poll",
|
||||
json=poll_data,
|
||||
headers=headers,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
job = response.json()
|
||||
logger.info(f"Received job: {job}")
|
||||
return job
|
||||
elif response.status_code == 204:
|
||||
return None
|
||||
else:
|
||||
logger.error(f"Poll failed: {response.status_code} - {response.text}")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error polling for jobs: {e}")
|
||||
return None
|
||||
|
||||
def main():
|
||||
"""Main miner loop"""
|
||||
logger.info("Starting Real GPU Miner Client on Host...")
|
||||
|
||||
# Check GPU availability
|
||||
gpu_info = get_gpu_info()
|
||||
if not gpu_info:
|
||||
logger.error("GPU not available, exiting")
|
||||
sys.exit(1)
|
||||
|
||||
logger.info(f"GPU detected: {gpu_info['name']} ({gpu_info['memory_total']}MB)")
|
||||
|
||||
# Check Ollama
|
||||
ollama_available, models = check_ollama()
|
||||
if not ollama_available:
|
||||
logger.error("Ollama not available - please install and start Ollama")
|
||||
sys.exit(1)
|
||||
|
||||
logger.info(f"Ollama models available: {', '.join(models)}")
|
||||
|
||||
# Wait for coordinator
|
||||
if not wait_for_coordinator():
|
||||
sys.exit(1)
|
||||
|
||||
# Register with coordinator
|
||||
session_token = register_miner()
|
||||
if not session_token:
|
||||
logger.error("Failed to register, exiting")
|
||||
sys.exit(1)
|
||||
|
||||
logger.info("Miner registered successfully, starting main loop...")
|
||||
|
||||
# Main loop
|
||||
last_heartbeat = 0
|
||||
last_poll = 0
|
||||
|
||||
try:
|
||||
while True:
|
||||
current_time = time.time()
|
||||
|
||||
# Send heartbeat
|
||||
if current_time - last_heartbeat >= HEARTBEAT_INTERVAL:
|
||||
send_heartbeat()
|
||||
last_heartbeat = current_time
|
||||
|
||||
# Poll for jobs
|
||||
if current_time - last_poll >= 3:
|
||||
job = poll_for_jobs()
|
||||
if job:
|
||||
# Execute the job with real GPU
|
||||
execute_job(job, models)
|
||||
last_poll = current_time
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
logger.info("Shutting down miner...")
|
||||
except Exception as e:
|
||||
logger.error(f"Error in main loop: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
3
scripts/gpu/gpu_miner_host_wrapper.sh
Executable file
3
scripts/gpu/gpu_miner_host_wrapper.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
# Wrapper script for GPU miner to ensure proper logging
|
||||
exec /home/oib/windsurf/aitbc/.venv/bin/python -u /home/oib/windsurf/aitbc/scripts/gpu/gpu_miner_host.py 2>&1
|
||||
329
scripts/gpu/gpu_miner_real.py
Normal file
329
scripts/gpu/gpu_miner_real.py
Normal file
@@ -0,0 +1,329 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Real GPU Miner Client for AITBC with Ollama integration
|
||||
"""
|
||||
|
||||
import json
|
||||
import time
|
||||
import httpx
|
||||
import logging
|
||||
import sys
|
||||
import subprocess
|
||||
import os
|
||||
from datetime import datetime
|
||||
|
||||
# Configuration
|
||||
COORDINATOR_URL = "http://127.0.0.1:8000"
|
||||
MINER_ID = "localhost-gpu-miner"
|
||||
AUTH_TOKEN = "REDACTED_MINER_KEY"
|
||||
HEARTBEAT_INTERVAL = 15
|
||||
MAX_RETRIES = 10
|
||||
RETRY_DELAY = 30
|
||||
|
||||
# Setup logging
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# GPU capabilities (RTX 4060 Ti)
|
||||
GPU_CAPABILITIES = {
|
||||
"gpu": {
|
||||
"model": "NVIDIA GeForce RTX 4060 Ti",
|
||||
"memory_gb": 16,
|
||||
"cuda_version": "12.4",
|
||||
"platform": "CUDA",
|
||||
"supported_tasks": ["inference", "training", "stable-diffusion", "llama"],
|
||||
"max_concurrent_jobs": 1
|
||||
}
|
||||
}
|
||||
|
||||
def check_gpu_available():
|
||||
"""Check if GPU is available"""
|
||||
try:
|
||||
result = subprocess.run(['nvidia-smi', '--query-gpu=name,memory.total', '--format=csv,noheader,nounits'],
|
||||
capture_output=True, text=True, timeout=5)
|
||||
if result.returncode == 0:
|
||||
gpu_info = result.stdout.strip().split(', ')
|
||||
logger.info(f"GPU detected: {gpu_info[0]}, Memory: {gpu_info[1]}MB")
|
||||
return True
|
||||
else:
|
||||
logger.error("nvidia-smi failed")
|
||||
return False
|
||||
except Exception as e:
|
||||
logger.error(f"GPU check failed: {e}")
|
||||
return False
|
||||
|
||||
def check_ollama():
|
||||
"""Check if Ollama is running"""
|
||||
try:
|
||||
response = httpx.get("http://localhost:11434/api/tags", timeout=5)
|
||||
if response.status_code == 200:
|
||||
models = response.json().get('models', [])
|
||||
logger.info(f"Ollama running with {len(models)} models")
|
||||
return True
|
||||
else:
|
||||
logger.error("Ollama not responding")
|
||||
return False
|
||||
except Exception as e:
|
||||
logger.error(f"Ollama check failed: {e}")
|
||||
return False
|
||||
|
||||
def wait_for_coordinator():
|
||||
"""Wait for coordinator to be available"""
|
||||
for i in range(MAX_RETRIES):
|
||||
try:
|
||||
response = httpx.get(f"{COORDINATOR_URL}/v1/health", timeout=5)
|
||||
if response.status_code == 200:
|
||||
logger.info("Coordinator is available!")
|
||||
return True
|
||||
except:
|
||||
pass
|
||||
|
||||
logger.info(f"Waiting for coordinator... ({i+1}/{MAX_RETRIES})")
|
||||
time.sleep(RETRY_DELAY)
|
||||
|
||||
logger.error("Coordinator not available after max retries")
|
||||
return False
|
||||
|
||||
def register_miner():
|
||||
"""Register the miner with the coordinator"""
|
||||
register_data = {
|
||||
"capabilities": GPU_CAPABILITIES,
|
||||
"concurrency": 1,
|
||||
"region": "localhost"
|
||||
}
|
||||
|
||||
headers = {
|
||||
"X-Api-Key": AUTH_TOKEN,
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
try:
|
||||
response = httpx.post(
|
||||
f"{COORDINATOR_URL}/v1/miners/register?miner_id={MINER_ID}",
|
||||
json=register_data,
|
||||
headers=headers,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
logger.info(f"Successfully registered miner: {data}")
|
||||
return data.get("session_token", "demo-token")
|
||||
else:
|
||||
logger.error(f"Registration failed: {response.status_code} - {response.text}")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Registration error: {e}")
|
||||
return None
|
||||
|
||||
def send_heartbeat():
|
||||
"""Send heartbeat to coordinator"""
|
||||
heartbeat_data = {
|
||||
"status": "active",
|
||||
"current_jobs": 0,
|
||||
"last_seen": datetime.utcnow().isoformat(),
|
||||
"gpu_utilization": 45, # Simulated
|
||||
"memory_used": 8192, # Simulated
|
||||
}
|
||||
|
||||
headers = {
|
||||
"X-Api-Key": AUTH_TOKEN,
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
try:
|
||||
response = httpx.post(
|
||||
f"{COORDINATOR_URL}/v1/miners/heartbeat?miner_id={MINER_ID}",
|
||||
json=heartbeat_data,
|
||||
headers=headers,
|
||||
timeout=5
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
logger.info("Heartbeat sent successfully")
|
||||
else:
|
||||
logger.error(f"Heartbeat failed: {response.status_code} - {response.text}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Heartbeat error: {e}")
|
||||
|
||||
def execute_job(job):
|
||||
"""Execute a job using GPU resources"""
|
||||
job_id = job.get('job_id')
|
||||
payload = job.get('payload', {})
|
||||
|
||||
logger.info(f"Executing job {job_id}: {payload}")
|
||||
|
||||
try:
|
||||
if payload.get('type') == 'inference':
|
||||
# Use Ollama for inference
|
||||
prompt = payload.get('prompt', '')
|
||||
model = payload.get('model', 'llama3.2:latest')
|
||||
|
||||
# Call Ollama API
|
||||
ollama_response = httpx.post(
|
||||
"http://localhost:11434/api/generate",
|
||||
json={
|
||||
"model": model,
|
||||
"prompt": prompt,
|
||||
"stream": False
|
||||
},
|
||||
timeout=60
|
||||
)
|
||||
|
||||
if ollama_response.status_code == 200:
|
||||
result = ollama_response.json()
|
||||
output = result.get('response', '')
|
||||
|
||||
# Submit result back to coordinator
|
||||
submit_result(job_id, {
|
||||
"status": "completed",
|
||||
"output": output,
|
||||
"model": model,
|
||||
"tokens_processed": result.get('eval_count', 0),
|
||||
"execution_time": result.get('total_duration', 0) / 1000000000, # Convert to seconds
|
||||
"gpu_used": True
|
||||
})
|
||||
|
||||
logger.info(f"Job {job_id} completed successfully")
|
||||
return True
|
||||
else:
|
||||
logger.error(f"Ollama error: {ollama_response.status_code}")
|
||||
submit_result(job_id, {
|
||||
"status": "failed",
|
||||
"error": f"Ollama error: {ollama_response.text}"
|
||||
})
|
||||
return False
|
||||
else:
|
||||
# Unsupported job type
|
||||
logger.error(f"Unsupported job type: {payload.get('type')}")
|
||||
submit_result(job_id, {
|
||||
"status": "failed",
|
||||
"error": f"Unsupported job type: {payload.get('type')}"
|
||||
})
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Job execution error: {e}")
|
||||
submit_result(job_id, {
|
||||
"status": "failed",
|
||||
"error": str(e)
|
||||
})
|
||||
return False
|
||||
|
||||
def submit_result(job_id, result):
|
||||
"""Submit job result to coordinator"""
|
||||
headers = {
|
||||
"X-Api-Key": AUTH_TOKEN,
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
try:
|
||||
response = httpx.post(
|
||||
f"{COORDINATOR_URL}/v1/jobs/{job_id}/result",
|
||||
json=result,
|
||||
headers=headers,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
logger.info(f"Result submitted for job {job_id}")
|
||||
else:
|
||||
logger.error(f"Result submission failed: {response.status_code} - {response.text}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Result submission error: {e}")
|
||||
|
||||
def poll_for_jobs():
|
||||
"""Poll for available jobs"""
|
||||
poll_data = {
|
||||
"miner_id": MINER_ID,
|
||||
"capabilities": GPU_CAPABILITIES,
|
||||
"max_wait_seconds": 5
|
||||
}
|
||||
|
||||
headers = {
|
||||
"X-Api-Key": AUTH_TOKEN,
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
try:
|
||||
response = httpx.post(
|
||||
f"{COORDINATOR_URL}/v1/miners/poll",
|
||||
json=poll_data,
|
||||
headers=headers,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
job = response.json()
|
||||
logger.info(f"Received job: {job}")
|
||||
return job
|
||||
elif response.status_code == 204:
|
||||
logger.info("No jobs available")
|
||||
return None
|
||||
else:
|
||||
logger.error(f"Poll failed: {response.status_code} - {response.text}")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error polling for jobs: {e}")
|
||||
return None
|
||||
|
||||
def main():
|
||||
"""Main miner loop"""
|
||||
logger.info("Starting Real GPU Miner Client...")
|
||||
|
||||
# Check GPU availability (optional)
|
||||
gpu_available = check_gpu_available()
|
||||
if not gpu_available:
|
||||
logger.warning("GPU not available - will run in CPU mode")
|
||||
|
||||
# Check Ollama
|
||||
if not check_ollama():
|
||||
logger.warning("Ollama not available - inference jobs will fail")
|
||||
|
||||
# Wait for coordinator
|
||||
if not wait_for_coordinator():
|
||||
sys.exit(1)
|
||||
|
||||
# Register with coordinator
|
||||
session_token = register_miner()
|
||||
if not session_token:
|
||||
logger.error("Failed to register, exiting")
|
||||
sys.exit(1)
|
||||
|
||||
logger.info("Miner registered successfully, starting main loop...")
|
||||
|
||||
# Main loop
|
||||
last_heartbeat = 0
|
||||
last_poll = 0
|
||||
|
||||
try:
|
||||
while True:
|
||||
current_time = time.time()
|
||||
|
||||
# Send heartbeat
|
||||
if current_time - last_heartbeat >= HEARTBEAT_INTERVAL:
|
||||
send_heartbeat()
|
||||
last_heartbeat = current_time
|
||||
|
||||
# Poll for jobs
|
||||
if current_time - last_poll >= 3:
|
||||
job = poll_for_jobs()
|
||||
if job:
|
||||
# Execute the job
|
||||
execute_job(job)
|
||||
last_poll = current_time
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
logger.info("Shutting down miner...")
|
||||
except Exception as e:
|
||||
logger.error(f"Error in main loop: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
299
scripts/gpu/gpu_miner_simple.py
Normal file
299
scripts/gpu/gpu_miner_simple.py
Normal file
@@ -0,0 +1,299 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Simple GPU Miner Client for AITBC - simulates GPU work
|
||||
"""
|
||||
|
||||
import json
|
||||
import time
|
||||
import httpx
|
||||
import logging
|
||||
import sys
|
||||
import subprocess
|
||||
from datetime import datetime
|
||||
|
||||
# Configuration
|
||||
COORDINATOR_URL = "http://127.0.0.1:8000"
|
||||
MINER_ID = "localhost-gpu-miner"
|
||||
AUTH_TOKEN = "REDACTED_MINER_KEY"
|
||||
HEARTBEAT_INTERVAL = 15
|
||||
MAX_RETRIES = 10
|
||||
RETRY_DELAY = 30
|
||||
|
||||
# Setup logging
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# GPU capabilities (simulated)
|
||||
GPU_CAPABILITIES = {
|
||||
"gpu": {
|
||||
"model": "NVIDIA GeForce RTX 4060 Ti",
|
||||
"memory_gb": 16,
|
||||
"cuda_version": "12.4",
|
||||
"platform": "CUDA",
|
||||
"supported_tasks": ["inference", "training", "stable-diffusion", "llama"],
|
||||
"max_concurrent_jobs": 1
|
||||
}
|
||||
}
|
||||
|
||||
def simulate_gpu_work(prompt, duration=3):
|
||||
"""Simulate GPU processing work"""
|
||||
logger.info(f"Simulating GPU work for: '{prompt}'")
|
||||
|
||||
# Simulate processing time
|
||||
time.sleep(duration)
|
||||
|
||||
# Generate a simple response based on the prompt
|
||||
if "hello" in prompt.lower():
|
||||
response = "Hello! I'm an AI assistant running on the AITBC network. Your request was processed by a GPU miner."
|
||||
elif "ai" in prompt.lower():
|
||||
response = "AI (Artificial Intelligence) is the simulation of human intelligence in machines that are programmed to think and learn."
|
||||
elif "blockchain" in prompt.lower():
|
||||
response = "Blockchain is a distributed ledger technology that maintains a secure and decentralized record of transactions."
|
||||
else:
|
||||
response = f"Processed request: {prompt}. This is a simulated GPU response from the AITBC network."
|
||||
|
||||
return response
|
||||
|
||||
def wait_for_coordinator():
|
||||
"""Wait for coordinator to be available"""
|
||||
for i in range(MAX_RETRIES):
|
||||
try:
|
||||
response = httpx.get(f"{COORDINATOR_URL}/v1/health", timeout=5)
|
||||
if response.status_code == 200:
|
||||
logger.info("Coordinator is available!")
|
||||
return True
|
||||
except:
|
||||
pass
|
||||
|
||||
logger.info(f"Waiting for coordinator... ({i+1}/{MAX_RETRIES})")
|
||||
time.sleep(RETRY_DELAY)
|
||||
|
||||
logger.error("Coordinator not available after max retries")
|
||||
return False
|
||||
|
||||
def register_miner():
|
||||
"""Register the miner with the coordinator"""
|
||||
register_data = {
|
||||
"capabilities": GPU_CAPABILITIES,
|
||||
"concurrency": 1,
|
||||
"region": "localhost"
|
||||
}
|
||||
|
||||
headers = {
|
||||
"X-Api-Key": AUTH_TOKEN,
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
try:
|
||||
response = httpx.post(
|
||||
f"{COORDINATOR_URL}/v1/miners/register?miner_id={MINER_ID}",
|
||||
json=register_data,
|
||||
headers=headers,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
logger.info(f"Successfully registered miner: {data}")
|
||||
return data.get("session_token", "demo-token")
|
||||
else:
|
||||
logger.error(f"Registration failed: {response.status_code} - {response.text}")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Registration error: {e}")
|
||||
return None
|
||||
|
||||
def send_heartbeat():
|
||||
"""Send heartbeat to coordinator"""
|
||||
heartbeat_data = {
|
||||
"status": "active",
|
||||
"current_jobs": 0,
|
||||
"last_seen": datetime.utcnow().isoformat(),
|
||||
"gpu_utilization": 45, # Simulated
|
||||
"memory_used": 8192, # Simulated
|
||||
}
|
||||
|
||||
headers = {
|
||||
"X-Api-Key": AUTH_TOKEN,
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
try:
|
||||
response = httpx.post(
|
||||
f"{COORDINATOR_URL}/v1/miners/heartbeat?miner_id={MINER_ID}",
|
||||
json=heartbeat_data,
|
||||
headers=headers,
|
||||
timeout=5
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
logger.info("Heartbeat sent successfully")
|
||||
else:
|
||||
logger.error(f"Heartbeat failed: {response.status_code} - {response.text}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Heartbeat error: {e}")
|
||||
|
||||
def execute_job(job):
|
||||
"""Execute a job using simulated GPU processing"""
|
||||
job_id = job.get('job_id')
|
||||
payload = job.get('payload', {})
|
||||
|
||||
logger.info(f"Executing job {job_id}: {payload}")
|
||||
|
||||
try:
|
||||
if payload.get('type') == 'inference':
|
||||
# Get the prompt
|
||||
prompt = payload.get('prompt', '')
|
||||
|
||||
# Simulate GPU processing
|
||||
logger.info(f"Processing with GPU...")
|
||||
result_text = simulate_gpu_work(prompt, duration=3)
|
||||
|
||||
# Submit result back to coordinator
|
||||
submit_result(job_id, {
|
||||
"result": {
|
||||
"status": "completed",
|
||||
"output": result_text,
|
||||
"model": "simulated-gpu",
|
||||
"tokens_processed": len(result_text.split()),
|
||||
"execution_time": 3.0,
|
||||
"gpu_used": True
|
||||
},
|
||||
"metrics": {
|
||||
"gpu_utilization": 85,
|
||||
"memory_used": 2048,
|
||||
"power_consumption": 250
|
||||
}
|
||||
})
|
||||
|
||||
logger.info(f"Job {job_id} completed successfully")
|
||||
return True
|
||||
else:
|
||||
# Unsupported job type
|
||||
logger.error(f"Unsupported job type: {payload.get('type')}")
|
||||
submit_result(job_id, {
|
||||
"result": {
|
||||
"status": "failed",
|
||||
"error": f"Unsupported job type: {payload.get('type')}"
|
||||
}
|
||||
})
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Job execution error: {e}")
|
||||
submit_result(job_id, {
|
||||
"result": {
|
||||
"status": "failed",
|
||||
"error": str(e)
|
||||
}
|
||||
})
|
||||
return False
|
||||
|
||||
def submit_result(job_id, result):
|
||||
"""Submit job result to coordinator"""
|
||||
headers = {
|
||||
"X-Api-Key": AUTH_TOKEN,
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
try:
|
||||
response = httpx.post(
|
||||
f"{COORDINATOR_URL}/v1/miners/{job_id}/result",
|
||||
json=result,
|
||||
headers=headers,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
logger.info(f"Result submitted for job {job_id}")
|
||||
else:
|
||||
logger.error(f"Result submission failed: {response.status_code} - {response.text}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Result submission error: {e}")
|
||||
|
||||
def poll_for_jobs():
|
||||
"""Poll for available jobs"""
|
||||
poll_data = {
|
||||
"miner_id": MINER_ID,
|
||||
"capabilities": GPU_CAPABILITIES,
|
||||
"max_wait_seconds": 5
|
||||
}
|
||||
|
||||
headers = {
|
||||
"X-Api-Key": AUTH_TOKEN,
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
try:
|
||||
response = httpx.post(
|
||||
f"{COORDINATOR_URL}/v1/miners/poll",
|
||||
json=poll_data,
|
||||
headers=headers,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
job = response.json()
|
||||
logger.info(f"Received job: {job}")
|
||||
return job
|
||||
elif response.status_code == 204:
|
||||
logger.info("No jobs available")
|
||||
return None
|
||||
else:
|
||||
logger.error(f"Poll failed: {response.status_code} - {response.text}")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error polling for jobs: {e}")
|
||||
return None
|
||||
|
||||
def main():
|
||||
"""Main miner loop"""
|
||||
logger.info("Starting Simple GPU Miner Client...")
|
||||
|
||||
# Wait for coordinator
|
||||
if not wait_for_coordinator():
|
||||
sys.exit(1)
|
||||
|
||||
# Register with coordinator
|
||||
session_token = register_miner()
|
||||
if not session_token:
|
||||
logger.error("Failed to register, exiting")
|
||||
sys.exit(1)
|
||||
|
||||
logger.info("Miner registered successfully, starting main loop...")
|
||||
|
||||
# Main loop
|
||||
last_heartbeat = 0
|
||||
last_poll = 0
|
||||
|
||||
try:
|
||||
while True:
|
||||
current_time = time.time()
|
||||
|
||||
# Send heartbeat
|
||||
if current_time - last_heartbeat >= HEARTBEAT_INTERVAL:
|
||||
send_heartbeat()
|
||||
last_heartbeat = current_time
|
||||
|
||||
# Poll for jobs
|
||||
if current_time - last_poll >= 3:
|
||||
job = poll_for_jobs()
|
||||
if job:
|
||||
# Execute the job
|
||||
execute_job(job)
|
||||
last_poll = current_time
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
logger.info("Shutting down miner...")
|
||||
except Exception as e:
|
||||
logger.error(f"Error in main loop: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
210
scripts/gpu/gpu_miner_with_wait.py
Normal file
210
scripts/gpu/gpu_miner_with_wait.py
Normal file
@@ -0,0 +1,210 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
GPU Miner Client with retry logic for AITBC
|
||||
"""
|
||||
|
||||
import json
|
||||
import time
|
||||
import httpx
|
||||
import logging
|
||||
import sys
|
||||
from datetime import datetime
|
||||
|
||||
# Configuration
|
||||
COORDINATOR_URL = "http://127.0.0.1:8000"
|
||||
MINER_ID = "localhost-gpu-miner"
|
||||
AUTH_TOKEN = "REDACTED_MINER_KEY"
|
||||
HEARTBEAT_INTERVAL = 15
|
||||
MAX_RETRIES = 10
|
||||
RETRY_DELAY = 30
|
||||
|
||||
# Setup logging
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# GPU capabilities (RTX 4060 Ti)
|
||||
GPU_CAPABILITIES = {
|
||||
"gpu": {
|
||||
"model": "NVIDIA GeForce RTX 4060 Ti",
|
||||
"memory_gb": 16,
|
||||
"cuda_version": "12.4",
|
||||
"compute_capability": "8.9",
|
||||
"driver_version": "550.163.01"
|
||||
},
|
||||
"compute": {
|
||||
"type": "GPU",
|
||||
"platform": "CUDA",
|
||||
"supported_tasks": ["inference", "training", "stable-diffusion", "llama"],
|
||||
"max_concurrent_jobs": 1
|
||||
}
|
||||
}
|
||||
|
||||
def wait_for_coordinator():
|
||||
"""Wait for coordinator to be available"""
|
||||
for i in range(MAX_RETRIES):
|
||||
try:
|
||||
response = httpx.get(f"{COORDINATOR_URL}/v1/health", timeout=5)
|
||||
if response.status_code == 200:
|
||||
logger.info("Coordinator is available!")
|
||||
return True
|
||||
except:
|
||||
pass
|
||||
|
||||
logger.info(f"Waiting for coordinator... ({i+1}/{MAX_RETRIES})")
|
||||
time.sleep(RETRY_DELAY)
|
||||
|
||||
logger.error("Coordinator not available after max retries")
|
||||
return False
|
||||
|
||||
def register_miner():
|
||||
"""Register the miner with the coordinator"""
|
||||
register_data = {
|
||||
"capabilities": GPU_CAPABILITIES,
|
||||
"concurrency": 1,
|
||||
"region": "localhost"
|
||||
}
|
||||
|
||||
headers = {
|
||||
"X-Api-Key": AUTH_TOKEN,
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
try:
|
||||
response = httpx.post(
|
||||
f"{COORDINATOR_URL}/v1/miners/register?miner_id={MINER_ID}",
|
||||
json=register_data,
|
||||
headers=headers,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
logger.info(f"Successfully registered miner: {data}")
|
||||
# Don't require session_token for demo registry
|
||||
return data.get("session_token", "demo-token")
|
||||
else:
|
||||
logger.error(f"Registration failed: {response.status_code} - {response.text}")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error registering miner: {e}")
|
||||
return None
|
||||
|
||||
def send_heartbeat():
|
||||
"""Send heartbeat to coordinator"""
|
||||
heartbeat_data = {
|
||||
"inflight": 0,
|
||||
"status": "ONLINE",
|
||||
"metadata": {
|
||||
"last_seen": datetime.utcnow().isoformat(),
|
||||
"gpu_utilization": 9,
|
||||
"gpu_memory_used": 2682,
|
||||
"gpu_temperature": 43
|
||||
}
|
||||
}
|
||||
|
||||
headers = {
|
||||
"X-Api-Key": AUTH_TOKEN,
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
try:
|
||||
response = httpx.post(
|
||||
f"{COORDINATOR_URL}/v1/miners/heartbeat?miner_id={MINER_ID}",
|
||||
json=heartbeat_data,
|
||||
headers=headers,
|
||||
timeout=5
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
logger.info("Heartbeat sent successfully")
|
||||
else:
|
||||
logger.error(f"Heartbeat failed: {response.status_code} - {response.text}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error sending heartbeat: {e}")
|
||||
|
||||
def poll_for_jobs():
|
||||
"""Poll for available jobs"""
|
||||
poll_data = {
|
||||
"max_wait_seconds": 5
|
||||
}
|
||||
|
||||
headers = {
|
||||
"X-Api-Key": AUTH_TOKEN,
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
try:
|
||||
response = httpx.post(
|
||||
f"{COORDINATOR_URL}/v1/miners/poll",
|
||||
json=poll_data,
|
||||
headers=headers,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
job = response.json()
|
||||
logger.info(f"Received job: {job}")
|
||||
return job
|
||||
elif response.status_code == 204:
|
||||
logger.info("No jobs available")
|
||||
return None
|
||||
elif response.status_code in (404, 405):
|
||||
# Coordinator/registry may not implement job polling (e.g. demo registry).
|
||||
# Keep running (heartbeats still work) but don't spam error logs.
|
||||
return None
|
||||
else:
|
||||
logger.error(f"Poll failed: {response.status_code} - {response.text}")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error polling for jobs: {e}")
|
||||
return None
|
||||
|
||||
def main():
|
||||
"""Main miner loop"""
|
||||
logger.info("Starting GPU Miner Client...")
|
||||
|
||||
# Wait for coordinator
|
||||
if not wait_for_coordinator():
|
||||
sys.exit(1)
|
||||
|
||||
# Register with coordinator
|
||||
session_token = register_miner()
|
||||
if not session_token:
|
||||
logger.error("Failed to register, exiting")
|
||||
sys.exit(1)
|
||||
|
||||
logger.info("Miner registered successfully, starting main loop...")
|
||||
|
||||
# Main loop
|
||||
last_heartbeat = 0
|
||||
last_poll = 0
|
||||
|
||||
try:
|
||||
while True:
|
||||
current_time = time.time()
|
||||
|
||||
# Send heartbeat
|
||||
if current_time - last_heartbeat >= HEARTBEAT_INTERVAL:
|
||||
send_heartbeat()
|
||||
last_heartbeat = current_time
|
||||
|
||||
# Poll for jobs
|
||||
if current_time - last_poll >= 3:
|
||||
job = poll_for_jobs()
|
||||
if job:
|
||||
logger.info(f"Would execute job: {job}")
|
||||
last_poll = current_time
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
logger.info("Shutting down miner...")
|
||||
except Exception as e:
|
||||
logger.error(f"Error in main loop: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
72
scripts/gpu/gpu_registry_demo.py
Normal file
72
scripts/gpu/gpu_registry_demo.py
Normal file
@@ -0,0 +1,72 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Simple GPU Registry Server for demonstration
|
||||
"""
|
||||
|
||||
from fastapi import FastAPI, HTTPException
|
||||
from pydantic import BaseModel
|
||||
from typing import Dict, Any, Optional
|
||||
import uvicorn
|
||||
from datetime import datetime
|
||||
|
||||
app = FastAPI(title="GPU Registry Demo")
|
||||
|
||||
# In-memory storage
|
||||
registered_gpus: Dict[str, Dict] = {}
|
||||
|
||||
class GPURegistration(BaseModel):
|
||||
capabilities: Dict[str, Any]
|
||||
concurrency: int = 1
|
||||
region: Optional[str] = None
|
||||
|
||||
class Heartbeat(BaseModel):
|
||||
inflight: int = 0
|
||||
status: str = "ONLINE"
|
||||
metadata: Dict[str, Any] = {}
|
||||
|
||||
@app.get("/")
|
||||
async def root():
|
||||
return {"message": "GPU Registry Demo", "registered_gpus": len(registered_gpus)}
|
||||
|
||||
@app.get("/health")
|
||||
async def health():
|
||||
return {"status": "ok"}
|
||||
|
||||
@app.post("/miners/register")
|
||||
async def register_gpu(miner_id: str, gpu_data: GPURegistration):
|
||||
"""Register a GPU miner"""
|
||||
registered_gpus[miner_id] = {
|
||||
"id": miner_id,
|
||||
"registered_at": datetime.utcnow().isoformat(),
|
||||
"last_heartbeat": datetime.utcnow().isoformat(),
|
||||
**gpu_data.dict()
|
||||
}
|
||||
return {"status": "ok", "message": f"GPU {miner_id} registered successfully"}
|
||||
|
||||
@app.post("/miners/heartbeat")
|
||||
async def heartbeat(miner_id: str, heartbeat_data: Heartbeat):
|
||||
"""Receive heartbeat from GPU miner"""
|
||||
if miner_id not in registered_gpus:
|
||||
raise HTTPException(status_code=404, detail="GPU not registered")
|
||||
|
||||
registered_gpus[miner_id]["last_heartbeat"] = datetime.utcnow().isoformat()
|
||||
registered_gpus[miner_id]["status"] = heartbeat_data.status
|
||||
registered_gpus[miner_id]["metadata"] = heartbeat_data.metadata
|
||||
|
||||
return {"status": "ok"}
|
||||
|
||||
@app.get("/miners/list")
|
||||
async def list_gpus():
|
||||
"""List all registered GPUs"""
|
||||
return {"gpus": list(registered_gpus.values())}
|
||||
|
||||
@app.get("/miners/{miner_id}")
|
||||
async def get_gpu(miner_id: str):
|
||||
"""Get details of a specific GPU"""
|
||||
if miner_id not in registered_gpus:
|
||||
raise HTTPException(status_code=404, detail="GPU not registered")
|
||||
return registered_gpus[miner_id]
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Starting GPU Registry Demo on http://localhost:8091")
|
||||
uvicorn.run(app, host="0.0.0.0", port=8091)
|
||||
146
scripts/gpu/integrate_gpu_exchange.py
Normal file
146
scripts/gpu/integrate_gpu_exchange.py
Normal file
@@ -0,0 +1,146 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Integrate GPU Miner with existing Trade Exchange
|
||||
"""
|
||||
|
||||
import httpx
|
||||
import json
|
||||
import subprocess
|
||||
import time
|
||||
from datetime import datetime
|
||||
|
||||
# Configuration
|
||||
EXCHANGE_URL = "http://localhost:3002"
|
||||
GPU_REGISTRY_URL = "http://localhost:8091"
|
||||
|
||||
def update_exchange_with_gpu():
|
||||
"""Update the exchange frontend to show registered GPUs"""
|
||||
|
||||
# Read the exchange HTML
|
||||
with open('/home/oib/windsurf/aitbc/apps/trade-exchange/index.html', 'r') as f:
|
||||
html_content = f.read()
|
||||
|
||||
# Add GPU marketplace integration
|
||||
gpu_integration = """
|
||||
<script>
|
||||
// GPU Integration
|
||||
async function loadRealGPUOffers() {
|
||||
try {
|
||||
const response = await fetch('http://localhost:8091/miners/list');
|
||||
const data = await response.json();
|
||||
|
||||
if (data.gpus && data.gpus.length > 0) {
|
||||
displayRealGPUOffers(data.gpus);
|
||||
} else {
|
||||
displayDemoOffers();
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('Using demo GPU offers');
|
||||
displayDemoOffers();
|
||||
}
|
||||
}
|
||||
|
||||
function displayRealGPUOffers(gpus) {
|
||||
const container = document.getElementById('gpuList');
|
||||
container.innerHTML = '';
|
||||
|
||||
gpus.forEach(gpu => {
|
||||
const gpuCard = `
|
||||
<div class="bg-white rounded-lg shadow-lg p-6 card-hover">
|
||||
<div class="flex justify-between items-start mb-4">
|
||||
<h3 class="text-lg font-semibold">${gpu.capabilities.gpu.model}</h3>
|
||||
<span class="bg-green-100 text-green-800 px-2 py-1 rounded text-sm">Available</span>
|
||||
</div>
|
||||
<div class="space-y-2 text-sm text-gray-600 mb-4">
|
||||
<p><i data-lucide="monitor" class="w-4 h-4 inline mr-1"></i>Memory: ${gpu.capabilities.gpu.memory_gb} GB</p>
|
||||
<p><i data-lucide="zap" class="w-4 h-4 inline mr-1"></i>CUDA: ${gpu.capabilities.gpu.cuda_version}</p>
|
||||
<p><i data-lucide="cpu" class="w-4 h-4 inline mr-1"></i>Concurrency: ${gpu.concurrency}</p>
|
||||
<p><i data-lucide="map-pin" class="w-4 h-4 inline mr-1"></i>Region: ${gpu.region}</p>
|
||||
</div>
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-2xl font-bold text-purple-600">50 AITBC/hr</span>
|
||||
<button onclick="purchaseGPU('${gpu.id}')" class="bg-purple-600 text-white px-4 py-2 rounded hover:bg-purple-700 transition">
|
||||
Purchase
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
container.innerHTML += gpuCard;
|
||||
});
|
||||
|
||||
lucide.createIcons();
|
||||
}
|
||||
|
||||
// Override the loadGPUOffers function
|
||||
const originalLoadGPUOffers = loadGPUOffers;
|
||||
loadGPUOffers = loadRealGPUOffers;
|
||||
</script>
|
||||
"""
|
||||
|
||||
# Insert before closing body tag
|
||||
if '</body>' in html_content:
|
||||
html_content = html_content.replace('</body>', gpu_integration + '</body>')
|
||||
|
||||
# Write back to file
|
||||
with open('/home/oib/windsurf/aitbc/apps/trade-exchange/index.html', 'w') as f:
|
||||
f.write(html_content)
|
||||
|
||||
print("✅ Updated exchange with GPU integration!")
|
||||
else:
|
||||
print("❌ Could not find </body> tag in exchange HTML")
|
||||
|
||||
def create_gpu_api_endpoint():
|
||||
"""Create an API endpoint in the exchange to serve GPU data"""
|
||||
|
||||
api_code = """
|
||||
@app.get("/api/gpu/offers")
|
||||
async def get_gpu_offers():
|
||||
\"\"\"Get available GPU offers\"\"\"
|
||||
try:
|
||||
# Fetch from GPU registry
|
||||
response = httpx.get("http://localhost:8091/miners/list")
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
return {"offers": data.get("gpus", [])}
|
||||
except:
|
||||
pass
|
||||
|
||||
# Return demo data if registry not available
|
||||
return {
|
||||
"offers": [{
|
||||
"id": "demo-gpu-1",
|
||||
"model": "NVIDIA RTX 4060 Ti",
|
||||
"memory_gb": 16,
|
||||
"price_per_hour": 50,
|
||||
"available": True
|
||||
}]
|
||||
}
|
||||
"""
|
||||
|
||||
print("\n📝 To add GPU API endpoint to exchange, add this code to simple_exchange_api.py:")
|
||||
print(api_code)
|
||||
|
||||
def main():
|
||||
print("🔗 Integrating GPU Miner with Trade Exchange...")
|
||||
|
||||
# Update exchange frontend
|
||||
update_exchange_with_gpu()
|
||||
|
||||
# Show API integration code
|
||||
create_gpu_api_endpoint()
|
||||
|
||||
print("\n📊 Integration Summary:")
|
||||
print("1. ✅ Exchange frontend updated to show real GPUs")
|
||||
print("2. 📝 See above for API endpoint code")
|
||||
print("3. 🌐 Access the exchange at: http://localhost:3002")
|
||||
print("4. 🎯 GPU Registry available at: http://localhost:8091/miners/list")
|
||||
|
||||
print("\n🔄 To see the integrated GPU marketplace:")
|
||||
print("1. Restart the trade exchange if needed:")
|
||||
print(" cd /home/oib/windsurf/aitbc/apps/trade-exchange")
|
||||
print(" python simple_exchange_api.py")
|
||||
print("2. Open http://localhost:3002 in browser")
|
||||
print("3. Click 'Browse GPU Marketplace'")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
182
scripts/gpu/simple_gpu_miner.py
Normal file
182
scripts/gpu/simple_gpu_miner.py
Normal file
@@ -0,0 +1,182 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Simple GPU Miner Client for AITBC
|
||||
Registers GPU with coordinator and sends heartbeats
|
||||
"""
|
||||
|
||||
import json
|
||||
import time
|
||||
import httpx
|
||||
import logging
|
||||
from datetime import datetime
|
||||
|
||||
# Configuration
|
||||
COORDINATOR_URL = "http://localhost:8000"
|
||||
MINER_ID = "localhost-gpu-miner"
|
||||
AUTH_TOKEN = "REDACTED_MINER_KEY"
|
||||
HEARTBEAT_INTERVAL = 15
|
||||
|
||||
# Setup logging
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# GPU capabilities (RTX 4060 Ti)
|
||||
GPU_CAPABILITIES = {
|
||||
"gpu": {
|
||||
"model": "NVIDIA GeForce RTX 4060 Ti",
|
||||
"memory_gb": 16,
|
||||
"cuda_version": "12.4",
|
||||
"compute_capability": "8.9",
|
||||
"driver_version": "550.163.01"
|
||||
},
|
||||
"compute": {
|
||||
"type": "GPU",
|
||||
"platform": "CUDA",
|
||||
"supported_tasks": ["inference", "training", "stable-diffusion", "llama"],
|
||||
"max_concurrent_jobs": 1
|
||||
}
|
||||
}
|
||||
|
||||
def register_miner():
|
||||
"""Register the miner with the coordinator"""
|
||||
register_data = {
|
||||
"capabilities": GPU_CAPABILITIES,
|
||||
"concurrency": 1,
|
||||
"region": "localhost"
|
||||
}
|
||||
|
||||
headers = {
|
||||
"Authorization": f"Bearer {AUTH_TOKEN}",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
try:
|
||||
response = httpx.post(
|
||||
f"{COORDINATOR_URL}/miners/register",
|
||||
json=register_data,
|
||||
headers=headers,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
logger.info(f"Successfully registered miner: {data}")
|
||||
return data.get("session_token")
|
||||
else:
|
||||
logger.error(f"Registration failed: {response.status_code} - {response.text}")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error registering miner: {e}")
|
||||
return None
|
||||
|
||||
def send_heartbeat():
|
||||
"""Send heartbeat to coordinator"""
|
||||
heartbeat_data = {
|
||||
"inflight": 0,
|
||||
"status": "ONLINE",
|
||||
"metadata": {
|
||||
"last_seen": datetime.utcnow().isoformat(),
|
||||
"gpu_utilization": 9, # Current GPU utilization from nvidia-smi
|
||||
"gpu_memory_used": 2682, # MB
|
||||
"gpu_temperature": 43
|
||||
}
|
||||
}
|
||||
|
||||
headers = {
|
||||
"Authorization": f"Bearer {AUTH_TOKEN}",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
try:
|
||||
response = httpx.post(
|
||||
f"{COORDINATOR_URL}/miners/heartbeat",
|
||||
json=heartbeat_data,
|
||||
headers=headers,
|
||||
timeout=5
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
logger.info("Heartbeat sent successfully")
|
||||
else:
|
||||
logger.error(f"Heartbeat failed: {response.status_code} - {response.text}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error sending heartbeat: {e}")
|
||||
|
||||
def poll_for_jobs():
|
||||
"""Poll for available jobs"""
|
||||
poll_data = {
|
||||
"max_wait_seconds": 5
|
||||
}
|
||||
|
||||
headers = {
|
||||
"Authorization": f"Bearer {AUTH_TOKEN}",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
try:
|
||||
response = httpx.post(
|
||||
f"{COORDINATOR_URL}/miners/poll",
|
||||
json=poll_data,
|
||||
headers=headers,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
job = response.json()
|
||||
logger.info(f"Received job: {job}")
|
||||
return job
|
||||
elif response.status_code == 204:
|
||||
logger.info("No jobs available")
|
||||
return None
|
||||
else:
|
||||
logger.error(f"Poll failed: {response.status_code} - {response.text}")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error polling for jobs: {e}")
|
||||
return None
|
||||
|
||||
def main():
|
||||
"""Main miner loop"""
|
||||
logger.info("Starting GPU Miner Client...")
|
||||
|
||||
# Register with coordinator
|
||||
session_token = register_miner()
|
||||
if not session_token:
|
||||
logger.error("Failed to register, exiting")
|
||||
return
|
||||
|
||||
logger.info("Miner registered successfully, starting main loop...")
|
||||
|
||||
# Main loop
|
||||
last_heartbeat = 0
|
||||
last_poll = 0
|
||||
|
||||
try:
|
||||
while True:
|
||||
current_time = time.time()
|
||||
|
||||
# Send heartbeat
|
||||
if current_time - last_heartbeat >= HEARTBEAT_INTERVAL:
|
||||
send_heartbeat()
|
||||
last_heartbeat = current_time
|
||||
|
||||
# Poll for jobs
|
||||
if current_time - last_poll >= 3: # Poll every 3 seconds
|
||||
job = poll_for_jobs()
|
||||
if job:
|
||||
# TODO: Execute job
|
||||
logger.info(f"Would execute job: {job}")
|
||||
last_poll = current_time
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
logger.info("Shutting down miner...")
|
||||
except Exception as e:
|
||||
logger.error(f"Error in main loop: {e}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
32
scripts/gpu/start_gpu_miner.sh
Executable file
32
scripts/gpu/start_gpu_miner.sh
Executable file
@@ -0,0 +1,32 @@
|
||||
#!/bin/bash
|
||||
# Start GPU Miner Client
|
||||
|
||||
echo "=== AITBC GPU Miner Client Startup ==="
|
||||
echo "Starting GPU miner client..."
|
||||
echo ""
|
||||
|
||||
# Check if GPU is available
|
||||
if ! command -v nvidia-smi &> /dev/null; then
|
||||
echo "WARNING: nvidia-smi not found, GPU may not be available"
|
||||
fi
|
||||
|
||||
# Show GPU info
|
||||
if command -v nvidia-smi &> /dev/null; then
|
||||
echo "=== GPU Status ==="
|
||||
nvidia-smi --query-gpu=name,memory.used,memory.total,utilization.gpu,temperature.gpu --format=csv,noheader,nounits
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Check if coordinator is running
|
||||
echo "=== Checking Coordinator API ==="
|
||||
if curl -s http://localhost:8000/health > /dev/null 2>&1; then
|
||||
echo "✓ Coordinator API is running on port 8000"
|
||||
else
|
||||
echo "✗ Coordinator API is not accessible on port 8000"
|
||||
echo " The miner will wait for the coordinator to start..."
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=== Starting GPU Miner ==="
|
||||
cd /home/oib/windsurf/aitbc
|
||||
python3 gpu_miner_with_wait.py
|
||||
135
scripts/local-domain-proxy.py
Executable file
135
scripts/local-domain-proxy.py
Executable file
@@ -0,0 +1,135 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Local proxy to simulate domain routing for development
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import time
|
||||
import os
|
||||
import signal
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# Configuration
|
||||
DOMAIN = "aitbc.bubuit.net"
|
||||
SERVICES = {
|
||||
"api": {"port": 8000, "path": "/v1"},
|
||||
"rpc": {"port": 9080, "path": "/rpc"},
|
||||
"marketplace": {"port": 3001, "path": "/"},
|
||||
"exchange": {"port": 3002, "path": "/"},
|
||||
}
|
||||
|
||||
def start_services():
|
||||
"""Start all AITBC services"""
|
||||
print("🚀 Starting AITBC Services")
|
||||
print("=" * 40)
|
||||
|
||||
# Change to project directory
|
||||
os.chdir("/home/oib/windsurf/aitbc")
|
||||
|
||||
processes = {}
|
||||
|
||||
# Start Coordinator API
|
||||
print("\n1. Starting Coordinator API...")
|
||||
api_proc = subprocess.Popen([
|
||||
"python", "-m", "uvicorn",
|
||||
"src.app.main:app",
|
||||
"--host", "127.0.0.1",
|
||||
"--port", "8000"
|
||||
], cwd="apps/coordinator-api")
|
||||
processes["api"] = api_proc
|
||||
print(f" PID: {api_proc.pid}")
|
||||
|
||||
# Start Blockchain Node (if not running)
|
||||
print("\n2. Checking Blockchain Node...")
|
||||
result = subprocess.run(["lsof", "-i", ":9080"], capture_output=True)
|
||||
if not result.stdout:
|
||||
print(" Starting Blockchain Node...")
|
||||
node_proc = subprocess.Popen([
|
||||
"python", "-m", "uvicorn",
|
||||
"aitbc_chain.app:app",
|
||||
"--host", "127.0.0.1",
|
||||
"--port", "9080"
|
||||
], cwd="apps/blockchain-node")
|
||||
processes["blockchain"] = node_proc
|
||||
print(f" PID: {node_proc.pid}")
|
||||
else:
|
||||
print(" ✅ Already running")
|
||||
|
||||
# Start Marketplace UI
|
||||
print("\n3. Starting Marketplace UI...")
|
||||
market_proc = subprocess.Popen([
|
||||
"python", "server.py",
|
||||
"--port", "3001"
|
||||
], cwd="apps/marketplace-ui")
|
||||
processes["marketplace"] = market_proc
|
||||
print(f" PID: {market_proc.pid}")
|
||||
|
||||
# Start Trade Exchange
|
||||
print("\n4. Starting Trade Exchange...")
|
||||
exchange_proc = subprocess.Popen([
|
||||
"python", "server.py",
|
||||
"--port", "3002"
|
||||
], cwd="apps/trade-exchange")
|
||||
processes["exchange"] = exchange_proc
|
||||
print(f" PID: {exchange_proc.pid}")
|
||||
|
||||
# Wait for services to start
|
||||
print("\n⏳ Waiting for services to start...")
|
||||
time.sleep(5)
|
||||
|
||||
# Test endpoints
|
||||
print("\n🧪 Testing Services:")
|
||||
test_endpoints()
|
||||
|
||||
print("\n✅ All services started!")
|
||||
print("\n📋 Local URLs:")
|
||||
print(f" API: http://127.0.0.1:8000/v1")
|
||||
print(f" RPC: http://127.0.0.1:9080/rpc")
|
||||
print(f" Marketplace: http://127.0.0.1:3001")
|
||||
print(f" Exchange: http://127.0.0.1:3002")
|
||||
|
||||
print("\n🌐 Domain URLs (when proxied):")
|
||||
print(f" API: https://{DOMAIN}/api")
|
||||
print(f" RPC: https://{DOMAIN}/rpc")
|
||||
print(f" Marketplace: https://{DOMAIN}/Marketplace")
|
||||
print(f" Exchange: https://{DOMAIN}/Exchange")
|
||||
print(f" Admin: https://{DOMAIN}/admin")
|
||||
|
||||
print("\n🛑 Press Ctrl+C to stop all services")
|
||||
|
||||
try:
|
||||
# Keep running
|
||||
while True:
|
||||
time.sleep(1)
|
||||
except KeyboardInterrupt:
|
||||
print("\n\n🛑 Stopping services...")
|
||||
for name, proc in processes.items():
|
||||
print(f" Stopping {name}...")
|
||||
proc.terminate()
|
||||
proc.wait()
|
||||
print("✅ All services stopped!")
|
||||
|
||||
def test_endpoints():
|
||||
"""Test if services are responding"""
|
||||
import requests
|
||||
|
||||
endpoints = [
|
||||
("API Health", "http://127.0.0.1:8000/v1/health"),
|
||||
("Admin Stats", "http://127.0.0.1:8000/v1/admin/stats"),
|
||||
("Marketplace", "http://127.0.0.1:3001"),
|
||||
("Exchange", "http://127.0.0.1:3002"),
|
||||
]
|
||||
|
||||
for name, url in endpoints:
|
||||
try:
|
||||
if "admin" in url:
|
||||
response = requests.get(url, headers={"X-Api-Key": "REDACTED_ADMIN_KEY"}, timeout=2)
|
||||
else:
|
||||
response = requests.get(url, timeout=2)
|
||||
print(f" {name}: ✅ {response.status_code}")
|
||||
except Exception as e:
|
||||
print(f" {name}: ❌ {str(e)[:50]}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
start_services()
|
||||
98
scripts/manage_services.sh
Executable file
98
scripts/manage_services.sh
Executable file
@@ -0,0 +1,98 @@
|
||||
#!/bin/bash
|
||||
|
||||
# AITBC Service Management Script
|
||||
|
||||
case "$1" in
|
||||
status)
|
||||
echo "=== AITBC Service Status ==="
|
||||
for service in aitbc-coordinator-api aitbc-exchange-api aitbc-exchange-frontend aitbc-wallet aitbc-node; do
|
||||
status=$(sudo systemctl is-active $service 2>/dev/null || echo "inactive")
|
||||
enabled=$(sudo systemctl is-enabled $service 2>/dev/null || echo "disabled")
|
||||
echo "$service: $status ($enabled)"
|
||||
done
|
||||
;;
|
||||
|
||||
start)
|
||||
echo "Starting AITBC services..."
|
||||
sudo systemctl start aitbc-coordinator-api
|
||||
sudo systemctl start aitbc-exchange-api
|
||||
sudo systemctl start aitbc-exchange-frontend
|
||||
sudo systemctl start aitbc-wallet
|
||||
sudo systemctl start aitbc-node
|
||||
echo "Done!"
|
||||
;;
|
||||
|
||||
stop)
|
||||
echo "Stopping AITBC services..."
|
||||
sudo systemctl stop aitbc-coordinator-api
|
||||
sudo systemctl stop aitbc-exchange-api
|
||||
sudo systemctl stop aitbc-exchange-frontend
|
||||
sudo systemctl stop aitbc-wallet
|
||||
sudo systemctl stop aitbc-node
|
||||
echo "Done!"
|
||||
;;
|
||||
|
||||
restart)
|
||||
echo "Restarting AITBC services..."
|
||||
sudo systemctl restart aitbc-coordinator-api
|
||||
sudo systemctl restart aitbc-exchange-api
|
||||
sudo systemctl restart aitbc-exchange-frontend
|
||||
sudo systemctl restart aitbc-wallet
|
||||
sudo systemctl restart aitbc-node
|
||||
echo "Done!"
|
||||
;;
|
||||
|
||||
logs)
|
||||
if [ -z "$2" ]; then
|
||||
echo "Usage: $0 logs <service-name>"
|
||||
echo "Available services: coordinator-api, exchange-api, exchange-frontend, wallet, node"
|
||||
exit 1
|
||||
fi
|
||||
case "$2" in
|
||||
coordinator-api) sudo journalctl -u aitbc-coordinator-api -f ;;
|
||||
exchange-api) sudo journalctl -u aitbc-exchange-api -f ;;
|
||||
exchange-frontend) sudo journalctl -u aitbc-exchange-frontend -f ;;
|
||||
wallet) sudo journalctl -u aitbc-wallet -f ;;
|
||||
node) sudo journalctl -u aitbc-node -f ;;
|
||||
*) echo "Unknown service: $2" ;;
|
||||
esac
|
||||
;;
|
||||
|
||||
enable)
|
||||
echo "Enabling AITBC services to start on boot..."
|
||||
sudo systemctl enable aitbc-coordinator-api
|
||||
sudo systemctl enable aitbc-exchange-api
|
||||
sudo systemctl enable aitbc-exchange-frontend
|
||||
sudo systemctl enable aitbc-wallet
|
||||
sudo systemctl enable aitbc-node
|
||||
echo "Done!"
|
||||
;;
|
||||
|
||||
disable)
|
||||
echo "Disabling AITBC services from starting on boot..."
|
||||
sudo systemctl disable aitbc-coordinator-api
|
||||
sudo systemctl disable aitbc-exchange-api
|
||||
sudo systemctl disable aitbc-exchange-frontend
|
||||
sudo systemctl disable aitbc-wallet
|
||||
sudo systemctl disable aitbc-node
|
||||
echo "Done!"
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Usage: $0 {status|start|stop|restart|logs|enable|disable}"
|
||||
echo ""
|
||||
echo "Commands:"
|
||||
echo " status - Show status of all AITBC services"
|
||||
echo " start - Start all AITBC services"
|
||||
echo " stop - Stop all AITBC services"
|
||||
echo " restart - Restart all AITBC services"
|
||||
echo " logs - View logs for a specific service"
|
||||
echo " enable - Enable services to start on boot"
|
||||
echo " disable - Disable services from starting on boot"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " $0 status"
|
||||
echo " $0 logs exchange-api"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
115
scripts/miner_workflow.py
Normal file
115
scripts/miner_workflow.py
Normal file
@@ -0,0 +1,115 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Complete miner workflow - poll for jobs and assign proposer
|
||||
"""
|
||||
|
||||
import httpx
|
||||
import json
|
||||
import time
|
||||
from datetime import datetime
|
||||
|
||||
# Configuration
|
||||
COORDINATOR_URL = "http://localhost:8001"
|
||||
MINER_API_KEY = "REDACTED_MINER_KEY"
|
||||
MINER_ID = "localhost-gpu-miner"
|
||||
|
||||
def poll_and_accept_job():
|
||||
"""Poll for a job and accept it"""
|
||||
print("🔍 Polling for jobs...")
|
||||
|
||||
with httpx.Client() as client:
|
||||
# Poll for a job
|
||||
response = client.post(
|
||||
f"{COORDINATOR_URL}/v1/miners/poll",
|
||||
headers={
|
||||
"Content-Type": "application/json",
|
||||
"X-Api-Key": MINER_API_KEY
|
||||
},
|
||||
json={"max_wait_seconds": 5}
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
job = response.json()
|
||||
print(f"✅ Received job: {job['job_id']}")
|
||||
print(f" Task: {job['payload'].get('task', 'unknown')}")
|
||||
|
||||
# Simulate processing
|
||||
print("⚙️ Processing job...")
|
||||
time.sleep(2)
|
||||
|
||||
# Submit result
|
||||
result_data = {
|
||||
"result": {
|
||||
"status": "completed",
|
||||
"output": f"Job {job['job_id']} completed successfully",
|
||||
"execution_time_ms": 2000,
|
||||
"miner_id": MINER_ID
|
||||
},
|
||||
"metrics": {
|
||||
"compute_time": 2.0,
|
||||
"energy_used": 0.1
|
||||
}
|
||||
}
|
||||
|
||||
print(f"📤 Submitting result for job {job['job_id']}...")
|
||||
result_response = client.post(
|
||||
f"{COORDINATOR_URL}/v1/miners/{job['job_id']}/result",
|
||||
headers={
|
||||
"Content-Type": "application/json",
|
||||
"X-Api-Key": MINER_API_KEY
|
||||
},
|
||||
json=result_data
|
||||
)
|
||||
|
||||
if result_response.status_code == 200:
|
||||
print("✅ Result submitted successfully!")
|
||||
return job['job_id']
|
||||
else:
|
||||
print(f"❌ Failed to submit result: {result_response.status_code}")
|
||||
print(f" Response: {result_response.text}")
|
||||
return None
|
||||
|
||||
elif response.status_code == 204:
|
||||
print("ℹ️ No jobs available")
|
||||
return None
|
||||
else:
|
||||
print(f"❌ Failed to poll: {response.status_code}")
|
||||
return None
|
||||
|
||||
def check_block_proposer(job_id):
|
||||
"""Check if the block now has a proposer"""
|
||||
print(f"\n🔍 Checking proposer for job {job_id}...")
|
||||
|
||||
with httpx.Client() as client:
|
||||
response = client.get(f"{COORDINATOR_URL}/v1/explorer/blocks")
|
||||
|
||||
if response.status_code == 200:
|
||||
blocks = response.json()
|
||||
for block in blocks['items']:
|
||||
if block['hash'] == job_id:
|
||||
print(f"📦 Block Info:")
|
||||
print(f" Height: {block['height']}")
|
||||
print(f" Hash: {block['hash']}")
|
||||
print(f" Proposer: {block['proposer']}")
|
||||
print(f" Time: {block['timestamp']}")
|
||||
return block
|
||||
return None
|
||||
|
||||
def main():
|
||||
print("⛏️ AITBC Miner Workflow Demo")
|
||||
print(f" Miner ID: {MINER_ID}")
|
||||
print(f" Coordinator: {COORDINATOR_URL}")
|
||||
print()
|
||||
|
||||
# Poll and accept a job
|
||||
job_id = poll_and_accept_job()
|
||||
|
||||
if job_id:
|
||||
# Check if the block has a proposer now
|
||||
time.sleep(1) # Give the server a moment to update
|
||||
check_block_proposer(job_id)
|
||||
else:
|
||||
print("\n💡 Tip: Create a job first using example_client_remote.py")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
72
scripts/service/check-container.sh
Executable file
72
scripts/service/check-container.sh
Executable file
@@ -0,0 +1,72 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Check what's running in the aitbc container
|
||||
|
||||
echo "🔍 Checking AITBC Container Status"
|
||||
echo "================================="
|
||||
|
||||
# First, let's see if we can access the container
|
||||
if ! groups | grep -q incus; then
|
||||
echo "❌ You're not in the incus group!"
|
||||
echo "Run: sudo usermod -aG incus \$USER"
|
||||
echo "Then log out and log back in"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "📋 Container Info:"
|
||||
incus list | grep aitbc
|
||||
|
||||
echo ""
|
||||
echo "🔧 Services in container:"
|
||||
incus exec aitbc -- ps aux | grep -E "(uvicorn|python)" | grep -v grep || echo "No services running"
|
||||
|
||||
echo ""
|
||||
echo "🌐 Ports listening in container:"
|
||||
incus exec aitbc -- ss -tlnp | grep -E "(8000|9080|3001|3002)" || echo "No ports listening"
|
||||
|
||||
echo ""
|
||||
echo "📁 Nginx status:"
|
||||
incus exec aitbc -- systemctl status nginx --no-pager -l | head -20
|
||||
|
||||
echo ""
|
||||
echo "🔍 Nginx config test:"
|
||||
incus exec aitbc -- nginx -t
|
||||
|
||||
echo ""
|
||||
echo "📝 Nginx sites enabled:"
|
||||
incus exec aitbc -- ls -la /etc/nginx/sites-enabled/
|
||||
|
||||
echo ""
|
||||
echo "🚀 Starting services if needed..."
|
||||
|
||||
# Start the services
|
||||
incus exec aitbc -- bash -c "
|
||||
cd /home/oib/aitbc
|
||||
pkill -f uvicorn 2>/dev/null || true
|
||||
pkill -f server.py 2>/dev/null || true
|
||||
|
||||
# Start blockchain node
|
||||
cd apps/blockchain-node
|
||||
source ../../.venv/bin/activate
|
||||
python -m uvicorn aitbc_chain.app:app --host 0.0.0.0 --port 9080 &
|
||||
|
||||
# Start coordinator API
|
||||
cd ../coordinator-api
|
||||
source ../../.venv/bin/activate
|
||||
python -m uvicorn src.app.main:app --host 0.0.0.0 --port 8000 &
|
||||
|
||||
# Start marketplace UI
|
||||
cd ../marketplace-ui
|
||||
python server.py --port 3001 &
|
||||
|
||||
# Start trade exchange
|
||||
cd ../trade-exchange
|
||||
python server.py --port 3002 &
|
||||
|
||||
sleep 3
|
||||
echo 'Services started!'
|
||||
"
|
||||
|
||||
echo ""
|
||||
echo "✅ Done! Check services:"
|
||||
echo "incus exec aitbc -- ps aux | grep uvicorn"
|
||||
65
scripts/service/diagnose-services.sh
Executable file
65
scripts/service/diagnose-services.sh
Executable file
@@ -0,0 +1,65 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Diagnose AITBC services
|
||||
|
||||
echo "🔍 Diagnosing AITBC Services"
|
||||
echo "=========================="
|
||||
echo ""
|
||||
|
||||
# Check local services
|
||||
echo "📋 Local Services:"
|
||||
echo "Port 8000 (Coordinator API):"
|
||||
lsof -i :8000 2>/dev/null || echo " ❌ Not running"
|
||||
|
||||
echo "Port 9080 (Blockchain Node):"
|
||||
lsof -i :9080 2>/dev/null || echo " ❌ Not running"
|
||||
|
||||
echo "Port 3001 (Marketplace UI):"
|
||||
lsof -i :3001 2>/dev/null || echo " ❌ Not running"
|
||||
|
||||
echo "Port 3002 (Trade Exchange):"
|
||||
lsof -i :3002 2>/dev/null || echo " ❌ Not running"
|
||||
|
||||
echo ""
|
||||
echo "🌐 Testing Endpoints:"
|
||||
|
||||
# Test local endpoints
|
||||
echo "Local API Health:"
|
||||
curl -s http://127.0.0.1:8000/v1/health 2>/dev/null && echo " ✅ OK" || echo " ❌ Failed"
|
||||
|
||||
echo "Local Blockchain:"
|
||||
curl -s http://127.0.0.1:9080/rpc/head 2>/dev/null | head -c 50 && echo "..." || echo " ❌ Failed"
|
||||
|
||||
echo "Local Admin:"
|
||||
curl -s http://127.0.0.1:8000/v1/admin/stats 2>/dev/null | head -c 50 && echo "..." || echo " ❌ Failed"
|
||||
|
||||
echo ""
|
||||
echo "🌐 Remote Endpoints (via domain):"
|
||||
echo "Domain API Health:"
|
||||
curl -s https://aitbc.bubuit.net/health 2>/dev/null && echo " ✅ OK" || echo " ❌ Failed"
|
||||
|
||||
echo "Domain Admin:"
|
||||
curl -s https://aitbc.bubuit.net/admin/stats 2>/dev/null | head -c 50 && echo "..." || echo " ❌ Failed"
|
||||
|
||||
echo ""
|
||||
echo "🔧 Fixing common issues..."
|
||||
|
||||
# Stop any conflicting services
|
||||
echo "Stopping local services..."
|
||||
sudo fuser -k 8000/tcp 2>/dev/null || true
|
||||
sudo fuser -k 9080/tcp 2>/dev/null || true
|
||||
sudo fuser -k 3001/tcp 2>/dev/null || true
|
||||
sudo fuser -k 3002/tcp 2>/dev/null || true
|
||||
|
||||
echo ""
|
||||
echo "📝 Instructions:"
|
||||
echo "1. Make sure you're in the incus group: sudo usermod -aG incus \$USER"
|
||||
echo "2. Log out and log back in"
|
||||
echo "3. Run: incus exec aitbc -- bash"
|
||||
echo "4. Inside container, run: /home/oib/start_aitbc.sh"
|
||||
echo "5. Check services: ps aux | grep uvicorn"
|
||||
echo ""
|
||||
echo "If services are running in container but not accessible:"
|
||||
echo "1. Check port forwarding to 10.1.223.93"
|
||||
echo "2. Check nginx config in container"
|
||||
echo "3. Check firewall rules"
|
||||
58
scripts/service/fix-services.sh
Executable file
58
scripts/service/fix-services.sh
Executable file
@@ -0,0 +1,58 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Quick fix to start AITBC services in container
|
||||
|
||||
echo "🔧 Starting AITBC Services in Container"
|
||||
echo "====================================="
|
||||
|
||||
# First, let's manually start the services
|
||||
echo "1. Starting Coordinator API..."
|
||||
cd /home/oib/windsurf/aitbc/apps/coordinator-api
|
||||
source ../../.venv/bin/activate 2>/dev/null || source .venv/bin/activate
|
||||
python -m uvicorn src.app.main:app --host 0.0.0.0 --port 8000 &
|
||||
COORD_PID=$!
|
||||
|
||||
echo "2. Starting Blockchain Node..."
|
||||
cd ../blockchain-node
|
||||
python -m uvicorn aitbc_chain.app:app --host 0.0.0.0 --port 9080 &
|
||||
NODE_PID=$!
|
||||
|
||||
echo "3. Starting Marketplace UI..."
|
||||
cd ../marketplace-ui
|
||||
python server.py --port 3001 &
|
||||
MARKET_PID=$!
|
||||
|
||||
echo "4. Starting Trade Exchange..."
|
||||
cd ../trade-exchange
|
||||
python server.py --port 3002 &
|
||||
EXCHANGE_PID=$!
|
||||
|
||||
echo ""
|
||||
echo "✅ Services started!"
|
||||
echo "Coordinator API: http://127.0.0.1:8000"
|
||||
echo "Blockchain: http://127.0.0.1:9080"
|
||||
echo "Marketplace: http://127.0.0.1:3001"
|
||||
echo "Exchange: http://127.0.0.1:3002"
|
||||
echo ""
|
||||
echo "PIDs:"
|
||||
echo "Coordinator: $COORD_PID"
|
||||
echo "Blockchain: $NODE_PID"
|
||||
echo "Marketplace: $MARKET_PID"
|
||||
echo "Exchange: $EXCHANGE_PID"
|
||||
echo ""
|
||||
echo "To stop: kill $COORD_PID $NODE_PID $MARKET_PID $EXCHANGE_PID"
|
||||
|
||||
# Wait a bit for services to start
|
||||
sleep 3
|
||||
|
||||
# Test endpoints
|
||||
echo ""
|
||||
echo "🧪 Testing endpoints:"
|
||||
echo "API Health:"
|
||||
curl -s http://127.0.0.1:8000/v1/health | head -c 100
|
||||
|
||||
echo -e "\n\nAdmin Stats:"
|
||||
curl -s http://127.0.0.1:8000/v1/admin/stats -H "X-Api-Key: REDACTED_ADMIN_KEY" | head -c 100
|
||||
|
||||
echo -e "\n\nMarketplace Offers:"
|
||||
curl -s http://127.0.0.1:8000/v1/marketplace/offers | head -c 100
|
||||
129
scripts/service/run-local-services.sh
Executable file
129
scripts/service/run-local-services.sh
Executable file
@@ -0,0 +1,129 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Run AITBC services locally for domain access
|
||||
|
||||
set -e
|
||||
|
||||
echo "🚀 Starting AITBC Services for Domain Access"
|
||||
echo "=========================================="
|
||||
|
||||
# Kill any existing services
|
||||
echo "Cleaning up existing services..."
|
||||
sudo fuser -k 8000/tcp 2>/dev/null || true
|
||||
sudo fuser -k 9080/tcp 2>/dev/null || true
|
||||
sudo fuser -k 3001/tcp 2>/dev/null || true
|
||||
sudo fuser -k 3002/tcp 2>/dev/null || true
|
||||
pkill -f "uvicorn.*aitbc" 2>/dev/null || true
|
||||
pkill -f "server.py" 2>/dev/null || true
|
||||
|
||||
# Wait for ports to be free
|
||||
sleep 2
|
||||
|
||||
# Create logs directory
|
||||
mkdir -p logs
|
||||
|
||||
echo ""
|
||||
echo "📦 Starting Services..."
|
||||
|
||||
# Start Coordinator API
|
||||
echo "1. Starting Coordinator API (port 8000)..."
|
||||
cd apps/coordinator-api
|
||||
source ../.venv/bin/activate 2>/dev/null || python -m venv ../.venv && source ../.venv/bin/activate
|
||||
pip install -q -e . 2>/dev/null || true
|
||||
nohup python -m uvicorn src.app.main:app --host 0.0.0.0 --port 8000 > ../../logs/api.log 2>&1 &
|
||||
API_PID=$!
|
||||
echo " PID: $API_PID"
|
||||
|
||||
# Start Blockchain Node
|
||||
echo "2. Starting Blockchain Node (port 9080)..."
|
||||
cd ../blockchain-node
|
||||
nohup python -m uvicorn aitbc_chain.app:app --host 0.0.0.0 --port 9080 > ../../logs/blockchain.log 2>&1 &
|
||||
NODE_PID=$!
|
||||
echo " PID: $NODE_PID"
|
||||
|
||||
# Start Marketplace UI
|
||||
echo "3. Starting Marketplace UI (port 3001)..."
|
||||
cd ../marketplace-ui
|
||||
nohup python server.py --port 3001 > ../../logs/marketplace.log 2>&1 &
|
||||
MARKET_PID=$!
|
||||
echo " PID: $MARKET_PID"
|
||||
|
||||
# Start Trade Exchange
|
||||
echo "4. Starting Trade Exchange (port 3002)..."
|
||||
cd ../trade-exchange
|
||||
nohup python server.py --port 3002 > ../../logs/exchange.log 2>&1 &
|
||||
EXCHANGE_PID=$!
|
||||
echo " PID: $EXCHANGE_PID"
|
||||
|
||||
# Save PIDs for cleanup
|
||||
echo "$API_PID $NODE_PID $MARKET_PID $EXCHANGE_PID" > ../.service_pids
|
||||
|
||||
cd ..
|
||||
|
||||
# Wait for services to start
|
||||
echo ""
|
||||
echo "⏳ Waiting for services to initialize..."
|
||||
sleep 5
|
||||
|
||||
# Test services
|
||||
echo ""
|
||||
echo "🧪 Testing Services..."
|
||||
|
||||
echo -n "API Health: "
|
||||
if curl -s http://127.0.0.1:8000/v1/health > /dev/null; then
|
||||
echo "✅ OK"
|
||||
else
|
||||
echo "❌ Failed"
|
||||
fi
|
||||
|
||||
echo -n "Admin API: "
|
||||
if curl -s http://127.0.0.1:8000/v1/admin/stats -H "X-Api-Key: REDACTED_ADMIN_KEY" > /dev/null; then
|
||||
echo "✅ OK"
|
||||
else
|
||||
echo "❌ Failed"
|
||||
fi
|
||||
|
||||
echo -n "Blockchain: "
|
||||
if curl -s http://127.0.0.1:9080/rpc/head > /dev/null; then
|
||||
echo "✅ OK"
|
||||
else
|
||||
echo "❌ Failed"
|
||||
fi
|
||||
|
||||
echo -n "Marketplace: "
|
||||
if curl -s http://127.0.0.1:3001 > /dev/null; then
|
||||
echo "✅ OK"
|
||||
else
|
||||
echo "❌ Failed"
|
||||
fi
|
||||
|
||||
echo -n "Exchange: "
|
||||
if curl -s http://127.0.0.1:3002 > /dev/null; then
|
||||
echo "✅ OK"
|
||||
else
|
||||
echo "❌ Failed"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "✅ All services started!"
|
||||
echo ""
|
||||
echo "📋 Local URLs:"
|
||||
echo " API: http://127.0.0.1:8000/v1"
|
||||
echo " RPC: http://127.0.0.1:9080/rpc"
|
||||
echo " Marketplace: http://127.0.0.1:3001"
|
||||
echo " Exchange: http://127.0.0.1:3002"
|
||||
echo ""
|
||||
echo "🌐 Domain URLs (if nginx is configured):"
|
||||
echo " API: https://aitbc.bubuit.net/api"
|
||||
echo " Admin: https://aitbc.bubuit.net/admin"
|
||||
echo " RPC: https://aitbc.bubuit.net/rpc"
|
||||
echo " Marketplace: https://aitbc.bubuit.net/Marketplace"
|
||||
echo " Exchange: https://aitbc.bubuit.net/Exchange"
|
||||
echo ""
|
||||
echo "📝 Logs: ./logs/"
|
||||
echo "🛑 Stop services: ./stop-services.sh"
|
||||
echo ""
|
||||
echo "Press Ctrl+C to stop monitoring (services will keep running)"
|
||||
|
||||
# Monitor logs
|
||||
tail -f logs/*.log
|
||||
40
scripts/service/setup-production-assets.sh
Normal file
40
scripts/service/setup-production-assets.sh
Normal file
@@ -0,0 +1,40 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Download production assets locally
|
||||
echo "Setting up production assets..."
|
||||
|
||||
# Create assets directory
|
||||
mkdir -p /home/oib/windsurf/aitbc/assets/{css,js,icons}
|
||||
|
||||
# Download Tailwind CSS (production build)
|
||||
echo "Downloading Tailwind CSS..."
|
||||
curl -L https://unpkg.com/tailwindcss@3.4.0/lib/tailwind.js -o /home/oib/windsurf/aitbc/assets/js/tailwind.js
|
||||
|
||||
# Download Axios
|
||||
echo "Downloading Axios..."
|
||||
curl -L https://unpkg.com/axios@1.6.2/dist/axios.min.js -o /home/oib/windsurf/aitbc/assets/js/axios.min.js
|
||||
|
||||
# Download Lucide icons
|
||||
echo "Downloading Lucide..."
|
||||
curl -L https://unpkg.com/lucide@latest/dist/umd/lucide.js -o /home/oib/windsurf/aitbc/assets/js/lucide.js
|
||||
|
||||
# Create a custom Tailwind build with only used classes
|
||||
cat > /home/oib/windsurf/aitbc/assets/tailwind.config.js << 'EOF'
|
||||
module.exports = {
|
||||
content: [
|
||||
"./apps/trade-exchange/index.html",
|
||||
"./apps/marketplace-ui/index.html"
|
||||
],
|
||||
darkMode: 'class',
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
EOF
|
||||
|
||||
echo "Assets downloaded to /home/oib/windsurf/aitbc/assets/"
|
||||
echo "Update your HTML files to use local paths:"
|
||||
echo " - /assets/js/tailwind.js"
|
||||
echo " - /assets/js/axios.min.js"
|
||||
echo " - /assets/js/lucide.js"
|
||||
39
scripts/service/start_dashboard.sh
Normal file
39
scripts/service/start_dashboard.sh
Normal file
@@ -0,0 +1,39 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "=== Starting AITBC Miner Dashboard ==="
|
||||
echo ""
|
||||
|
||||
# Find available port
|
||||
PORT=8080
|
||||
while [ $PORT -le 8090 ]; do
|
||||
if ! netstat -tuln 2>/dev/null | grep -q ":$PORT "; then
|
||||
echo "✓ Found available port: $PORT"
|
||||
break
|
||||
fi
|
||||
echo "Port $port is in use, trying next..."
|
||||
PORT=$((PORT + 1))
|
||||
done
|
||||
|
||||
if [ $PORT -gt 8090 ]; then
|
||||
echo "❌ No available ports found between 8080-8090"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Start the dashboard
|
||||
echo "Starting dashboard on port $PORT..."
|
||||
nohup python3 -m http.server $PORT --bind 0.0.0.0 > dashboard.log 2>&1 &
|
||||
PID=$!
|
||||
|
||||
echo ""
|
||||
echo "✅ Dashboard is running!"
|
||||
echo ""
|
||||
echo "Access URLs:"
|
||||
echo " Local: http://localhost:$PORT"
|
||||
echo " Network: http://$(hostname -I | awk '{print $1}'):$PORT"
|
||||
echo ""
|
||||
echo "Dashboard file: miner-dashboard.html"
|
||||
echo "Process ID: $PID"
|
||||
echo "Log file: dashboard.log"
|
||||
echo ""
|
||||
echo "To stop: kill $PID"
|
||||
echo "To view logs: tail -f dashboard.log"
|
||||
30
scripts/service/stop-services.sh
Executable file
30
scripts/service/stop-services.sh
Executable file
@@ -0,0 +1,30 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Stop all AITBC services
|
||||
|
||||
echo "🛑 Stopping AITBC Services"
|
||||
echo "========================"
|
||||
|
||||
# Stop by PID if file exists
|
||||
if [ -f .service_pids ]; then
|
||||
PIDS=$(cat .service_pids)
|
||||
echo "Found PIDs: $PIDS"
|
||||
for PID in $PIDS; do
|
||||
if kill -0 $PID 2>/dev/null; then
|
||||
echo "Stopping PID $PID..."
|
||||
kill $PID
|
||||
fi
|
||||
done
|
||||
rm -f .service_pids
|
||||
fi
|
||||
|
||||
# Force kill any remaining services
|
||||
echo "Cleaning up any remaining processes..."
|
||||
sudo fuser -k 8000/tcp 2>/dev/null || true
|
||||
sudo fuser -k 9080/tcp 2>/dev/null || true
|
||||
sudo fuser -k 3001/tcp 2>/dev/null || true
|
||||
sudo fuser -k 3002/tcp 2>/dev/null || true
|
||||
pkill -f "uvicorn.*aitbc" 2>/dev/null || true
|
||||
pkill -f "server.py" 2>/dev/null || true
|
||||
|
||||
echo "✅ All services stopped!"
|
||||
49
scripts/setup_systemd.sh
Executable file
49
scripts/setup_systemd.sh
Executable file
@@ -0,0 +1,49 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Setup AITBC Systemd Services
|
||||
echo "🔧 Setting up AITBC systemd services..."
|
||||
|
||||
# Copy service files
|
||||
echo "📁 Copying service files..."
|
||||
sudo cp systemd/aitbc-*.service /etc/systemd/system/
|
||||
|
||||
# Reload systemd daemon
|
||||
echo "🔄 Reloading systemd daemon..."
|
||||
sudo systemctl daemon-reload
|
||||
|
||||
# Stop existing processes
|
||||
echo "⏹️ Stopping existing processes..."
|
||||
pkill -f "coordinator-api" || true
|
||||
pkill -f "simple_exchange_api.py" || true
|
||||
pkill -f "server.py --port 3002" || true
|
||||
pkill -f "wallet_daemon" || true
|
||||
pkill -f "node.main" || true
|
||||
|
||||
# Enable services
|
||||
echo "✅ Enabling services..."
|
||||
sudo systemctl enable aitbc-coordinator-api.service
|
||||
sudo systemctl enable aitbc-exchange-api.service
|
||||
sudo systemctl enable aitbc-exchange-frontend.service
|
||||
sudo systemctl enable aitbc-wallet.service
|
||||
sudo systemctl enable aitbc-node.service
|
||||
|
||||
# Start services
|
||||
echo "🚀 Starting services..."
|
||||
sudo systemctl start aitbc-coordinator-api.service
|
||||
sudo systemctl start aitbc-exchange-api.service
|
||||
sudo systemctl start aitbc-exchange-frontend.service
|
||||
sudo systemctl start aitbc-wallet.service
|
||||
sudo systemctl start aitbc-node.service
|
||||
|
||||
# Check status
|
||||
echo ""
|
||||
echo "📊 Service Status:"
|
||||
for service in aitbc-coordinator-api aitbc-exchange-api aitbc-exchange-frontend aitbc-wallet aitbc-node; do
|
||||
status=$(sudo systemctl is-active $service)
|
||||
echo " $service: $status"
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "📝 To view logs: sudo journalctl -u <service-name> -f"
|
||||
echo "📝 To restart: sudo systemctl restart <service-name>"
|
||||
echo "📝 To stop: sudo systemctl stop <service-name>"
|
||||
22
scripts/start_remote_tunnel.sh
Executable file
22
scripts/start_remote_tunnel.sh
Executable file
@@ -0,0 +1,22 @@
|
||||
#!/bin/bash
|
||||
# Start SSH tunnel to remote AITBC coordinator
|
||||
|
||||
echo "Starting SSH tunnel to remote AITBC coordinator..."
|
||||
|
||||
# Check if tunnel is already running
|
||||
if pgrep -f "ssh.*-L.*8001:localhost:8000.*aitbc" > /dev/null; then
|
||||
echo "✅ Tunnel is already running"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Start the tunnel
|
||||
ssh -f -N -L 8001:localhost:8000 aitbc
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✅ SSH tunnel established on port 8001"
|
||||
echo " Remote coordinator available at: http://localhost:8001"
|
||||
echo " Health check: curl http://localhost:8001/v1/health"
|
||||
else
|
||||
echo "❌ Failed to establish SSH tunnel"
|
||||
exit 1
|
||||
fi
|
||||
45
scripts/test/test_coordinator.py
Normal file
45
scripts/test/test_coordinator.py
Normal file
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test GPU registration with mock coordinator
|
||||
"""
|
||||
|
||||
import httpx
|
||||
import json
|
||||
|
||||
COORDINATOR_URL = "http://localhost:8090"
|
||||
|
||||
# Test available endpoints
|
||||
print("=== Testing Mock Coordinator Endpoints ===")
|
||||
endpoints = [
|
||||
"/",
|
||||
"/health",
|
||||
"/metrics",
|
||||
"/miners/register",
|
||||
"/miners/list",
|
||||
"/marketplace/offers"
|
||||
]
|
||||
|
||||
for endpoint in endpoints:
|
||||
try:
|
||||
response = httpx.get(f"{COORDINATOR_URL}{endpoint}", timeout=5)
|
||||
print(f"{endpoint}: {response.status_code}")
|
||||
if response.status_code == 200 and response.text:
|
||||
try:
|
||||
data = response.json()
|
||||
print(f" Response: {json.dumps(data, indent=2)[:200]}...")
|
||||
except:
|
||||
print(f" Response: {response.text[:100]}...")
|
||||
except Exception as e:
|
||||
print(f"{endpoint}: Error - {e}")
|
||||
|
||||
print("\n=== Checking OpenAPI Spec ===")
|
||||
try:
|
||||
response = httpx.get(f"{COORDINATOR_URL}/openapi.json", timeout=5)
|
||||
if response.status_code == 200:
|
||||
openapi = response.json()
|
||||
paths = list(openapi.get("paths", {}).keys())
|
||||
print(f"Available endpoints: {paths}")
|
||||
else:
|
||||
print(f"OpenAPI not available: {response.status_code}")
|
||||
except Exception as e:
|
||||
print(f"Error getting OpenAPI: {e}")
|
||||
63
scripts/test/test_host_miner.py
Normal file
63
scripts/test/test_host_miner.py
Normal file
@@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test script for host GPU miner
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import httpx
|
||||
|
||||
# Test GPU
|
||||
print("Testing GPU access...")
|
||||
result = subprocess.run(['nvidia-smi', '--query-gpu=name', '--format=csv,noheader,nounits'],
|
||||
capture_output=True, text=True)
|
||||
if result.returncode == 0:
|
||||
print(f"✅ GPU detected: {result.stdout.strip()}")
|
||||
else:
|
||||
print("❌ GPU not accessible")
|
||||
|
||||
# Test Ollama
|
||||
print("\nTesting Ollama...")
|
||||
try:
|
||||
response = httpx.get("http://localhost:11434/api/tags", timeout=5)
|
||||
if response.status_code == 200:
|
||||
models = response.json().get('models', [])
|
||||
print(f"✅ Ollama running with {len(models)} models")
|
||||
for m in models[:3]: # Show first 3 models
|
||||
print(f" - {m['name']}")
|
||||
else:
|
||||
print("❌ Ollama not responding")
|
||||
except Exception as e:
|
||||
print(f"❌ Ollama error: {e}")
|
||||
|
||||
# Test Coordinator
|
||||
print("\nTesting Coordinator...")
|
||||
try:
|
||||
response = httpx.get("http://127.0.0.1:8000/v1/health", timeout=5)
|
||||
if response.status_code == 200:
|
||||
print("✅ Coordinator is accessible")
|
||||
else:
|
||||
print("❌ Coordinator not responding")
|
||||
except Exception as e:
|
||||
print(f"❌ Coordinator error: {e}")
|
||||
|
||||
# Test Ollama inference
|
||||
print("\nTesting Ollama inference...")
|
||||
try:
|
||||
response = httpx.post(
|
||||
"http://localhost:11434/api/generate",
|
||||
json={
|
||||
"model": "llama3.2:latest",
|
||||
"prompt": "Say hello",
|
||||
"stream": False
|
||||
},
|
||||
timeout=10
|
||||
)
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
print(f"✅ Inference successful: {result.get('response', '')[:50]}...")
|
||||
else:
|
||||
print("❌ Inference failed")
|
||||
except Exception as e:
|
||||
print(f"❌ Inference error: {e}")
|
||||
|
||||
print("\n✅ All tests completed!")
|
||||
77
scripts/test/test_transactions_display.py
Executable file
77
scripts/test/test_transactions_display.py
Executable file
@@ -0,0 +1,77 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test if transactions are displaying on the explorer
|
||||
"""
|
||||
|
||||
import requests
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
def main():
|
||||
print("🔍 Testing Transaction Display on Explorer")
|
||||
print("=" * 60)
|
||||
|
||||
# Check API has transactions
|
||||
print("\n1. Checking API for transactions...")
|
||||
try:
|
||||
response = requests.get("https://aitbc.bubuit.net/api/explorer/transactions")
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
print(f"✅ API has {len(data['items'])} transactions")
|
||||
|
||||
if data['items']:
|
||||
first_tx = data['items'][0]
|
||||
print(f"\n First transaction:")
|
||||
print(f" Hash: {first_tx['hash']}")
|
||||
print(f" From: {first_tx['from']}")
|
||||
print(f" To: {first_tx.get('to', 'null')}")
|
||||
print(f" Value: {first_tx['value']}")
|
||||
print(f" Status: {first_tx['status']}")
|
||||
else:
|
||||
print(f"❌ API failed: {response.status_code}")
|
||||
return
|
||||
except Exception as e:
|
||||
print(f"❌ Error: {e}")
|
||||
return
|
||||
|
||||
# Check explorer page
|
||||
print("\n2. Checking explorer page...")
|
||||
try:
|
||||
response = requests.get("https://aitbc.bubuit.net/explorer/#/transactions")
|
||||
if response.status_code == 200:
|
||||
soup = BeautifulSoup(response.text, 'html.parser')
|
||||
|
||||
# Check if it says "mock data"
|
||||
if "mock data" in soup.text.lower():
|
||||
print("❌ Page still shows 'mock data' message")
|
||||
else:
|
||||
print("✅ No 'mock data' message found")
|
||||
|
||||
# Check for transactions table
|
||||
table = soup.find('tbody', {'id': 'transactions-table-body'})
|
||||
if table:
|
||||
rows = table.find_all('tr')
|
||||
if len(rows) > 0:
|
||||
if 'Loading' in rows[0].text:
|
||||
print("⏳ Still loading transactions...")
|
||||
elif 'No transactions' in rows[0].text:
|
||||
print("❌ No transactions displayed")
|
||||
else:
|
||||
print(f"✅ Found {len(rows)} transaction rows")
|
||||
else:
|
||||
print("❌ No transaction rows found")
|
||||
else:
|
||||
print("❌ Transactions table not found")
|
||||
else:
|
||||
print(f"❌ Failed to load page: {response.status_code}")
|
||||
except Exception as e:
|
||||
print(f"❌ Error: {e}")
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("\n💡 If transactions aren't showing, it might be because:")
|
||||
print(" 1. JavaScript is still loading")
|
||||
print(" 2. The API call is failing")
|
||||
print(" 3. The transactions have empty values")
|
||||
print("\n Try refreshing the page or check browser console for errors")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
91
scripts/test/verify_explorer_live.py
Executable file
91
scripts/test/verify_explorer_live.py
Executable file
@@ -0,0 +1,91 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Verify that the explorer is using live data instead of mock
|
||||
"""
|
||||
|
||||
import requests
|
||||
import json
|
||||
|
||||
def main():
|
||||
print("🔍 Verifying AITBC Explorer is using Live Data")
|
||||
print("=" * 60)
|
||||
|
||||
# Check API endpoint
|
||||
print("\n1. Testing API endpoint...")
|
||||
try:
|
||||
response = requests.get("https://aitbc.bubuit.net/api/explorer/blocks")
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
print(f"✅ API is working - Found {len(data['items'])} blocks")
|
||||
|
||||
# Show latest block
|
||||
if data['items']:
|
||||
latest = data['items'][0]
|
||||
print(f"\n Latest Block:")
|
||||
print(f" Height: {latest['height']}")
|
||||
print(f" Hash: {latest['hash']}")
|
||||
print(f" Proposer: {latest['proposer']}")
|
||||
print(f" Time: {latest['timestamp']}")
|
||||
else:
|
||||
print(f"❌ API failed: {response.status_code}")
|
||||
return
|
||||
except Exception as e:
|
||||
print(f"❌ API error: {e}")
|
||||
return
|
||||
|
||||
# Check explorer page
|
||||
print("\n2. Checking explorer configuration...")
|
||||
|
||||
# Get the JS file
|
||||
try:
|
||||
js_response = requests.get("https://aitbc.bubuit.net/explorer/assets/index-IsD_hiHT.js")
|
||||
if js_response.status_code == 200:
|
||||
js_content = js_response.text
|
||||
|
||||
# Check for live data mode
|
||||
if 'dataMode:"live"' in js_content:
|
||||
print("✅ Explorer is configured for LIVE data")
|
||||
elif 'dataMode:"mock"' in js_content:
|
||||
print("❌ Explorer is still using MOCK data")
|
||||
return
|
||||
else:
|
||||
print("⚠️ Could not determine data mode")
|
||||
except Exception as e:
|
||||
print(f"❌ Error checking JS: {e}")
|
||||
|
||||
# Check other endpoints
|
||||
print("\n3. Testing other endpoints...")
|
||||
|
||||
endpoints = [
|
||||
("/api/explorer/transactions", "Transactions"),
|
||||
("/api/explorer/addresses", "Addresses"),
|
||||
("/api/explorer/receipts", "Receipts")
|
||||
]
|
||||
|
||||
for endpoint, name in endpoints:
|
||||
try:
|
||||
response = requests.get(f"https://aitbc.bubuit.net{endpoint}")
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
print(f"✅ {name}: {len(data['items'])} items")
|
||||
else:
|
||||
print(f"❌ {name}: Failed ({response.status_code})")
|
||||
except Exception as e:
|
||||
print(f"❌ {name}: Error - {e}")
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("✅ Explorer is successfully using LIVE data!")
|
||||
print("\n📊 Live Data Sources:")
|
||||
print(" • Blocks: https://aitbc.bubuit.net/api/explorer/blocks")
|
||||
print(" • Transactions: https://aitbc.bubuit.net/api/explorer/transactions")
|
||||
print(" • Addresses: https://aitbc.bubuit.net/api/explorer/addresses")
|
||||
print(" • Receipts: https://aitbc.bubuit.net/api/explorer/receipts")
|
||||
|
||||
print("\n💡 Visitors to https://aitbc.bubuit.net/explorer/ will now see:")
|
||||
print(" • Real blockchain data")
|
||||
print(" • Actual transactions")
|
||||
print(" • Live network activity")
|
||||
print(" • No mock/sample data")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
35
scripts/test/verify_gpu_deployment.sh
Normal file
35
scripts/test/verify_gpu_deployment.sh
Normal file
@@ -0,0 +1,35 @@
|
||||
#!/bin/bash
|
||||
# Simple verification of GPU deployment in container
|
||||
|
||||
echo "🔍 Checking GPU deployment in AITBC container..."
|
||||
|
||||
# Check if services exist
|
||||
echo "1. Checking if services are installed..."
|
||||
if ssh aitbc 'systemctl list-unit-files | grep -E "aitbc-gpu" 2>/dev/null'; then
|
||||
echo "✅ GPU services found"
|
||||
else
|
||||
echo "❌ GPU services not found - need to deploy first"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check service status
|
||||
echo -e "\n2. Checking service status..."
|
||||
ssh aitbc 'sudo systemctl status aitbc-gpu-registry.service --no-pager --lines=3'
|
||||
ssh aitbc 'sudo systemctl status aitbc-gpu-miner.service --no-pager --lines=3'
|
||||
|
||||
# Check if ports are listening
|
||||
echo -e "\n3. Checking if GPU registry is listening..."
|
||||
if ssh aitbc 'ss -tlnp | grep :8091 2>/dev/null'; then
|
||||
echo "✅ GPU registry listening on port 8091"
|
||||
else
|
||||
echo "❌ GPU registry not listening"
|
||||
fi
|
||||
|
||||
# Check GPU registration
|
||||
echo -e "\n4. Checking GPU registration from container..."
|
||||
ssh aitbc 'curl -s http://127.0.0.1:8091/miners/list 2>/dev/null | python3 -c "import sys,json; data=json.load(sys.stdin); print(f\"Found {len(data.get(\"gpus\", []))} GPU(s)\")" 2>/dev/null || echo "Failed to get GPU list"'
|
||||
|
||||
echo -e "\n5. Checking from host (10.1.223.93)..."
|
||||
curl -s http://10.1.223.93:8091/miners/list 2>/dev/null | python3 -c "import sys,json; data=json.load(sys.stdin); print(f\"✅ From host: Found {len(data.get(\"gpus\", []))} GPU(s)\")" 2>/dev/null || echo "❌ Cannot access from host"
|
||||
|
||||
echo -e "\n✅ Verification complete!"
|
||||
84
scripts/test/verify_toggle_removed.py
Executable file
84
scripts/test/verify_toggle_removed.py
Executable file
@@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Verify that the data mode toggle button is removed from the explorer
|
||||
"""
|
||||
|
||||
import requests
|
||||
import re
|
||||
|
||||
def main():
|
||||
print("🔍 Verifying Data Mode Toggle is Removed")
|
||||
print("=" * 60)
|
||||
|
||||
# Get the explorer page
|
||||
print("\n1. Checking explorer page...")
|
||||
try:
|
||||
response = requests.get("https://aitbc.bubuit.net/explorer/")
|
||||
if response.status_code == 200:
|
||||
print("✅ Explorer page loaded")
|
||||
else:
|
||||
print(f"❌ Failed to load page: {response.status_code}")
|
||||
return
|
||||
except Exception as e:
|
||||
print(f"❌ Error: {e}")
|
||||
return
|
||||
|
||||
# Check for data mode toggle elements
|
||||
print("\n2. Checking for data mode toggle...")
|
||||
|
||||
html_content = response.text
|
||||
|
||||
# Check for toggle button
|
||||
if 'dataModeBtn' in html_content:
|
||||
print("❌ Data mode toggle button still present!")
|
||||
return
|
||||
else:
|
||||
print("✅ Data mode toggle button removed")
|
||||
|
||||
# Check for mode-button class
|
||||
if 'mode-button' in html_content:
|
||||
print("❌ Mode button class still found!")
|
||||
return
|
||||
else:
|
||||
print("✅ Mode button class removed")
|
||||
|
||||
# Check for data-mode-toggle
|
||||
if 'data-mode-toggle' in html_content:
|
||||
print("❌ Data mode toggle component still present!")
|
||||
return
|
||||
else:
|
||||
print("✅ Data mode toggle component removed")
|
||||
|
||||
# Check JS file
|
||||
print("\n3. Checking JavaScript file...")
|
||||
try:
|
||||
js_response = requests.get("https://aitbc.bubuit.net/explorer/assets/index-7nlLaz1v.js")
|
||||
if js_response.status_code == 200:
|
||||
js_content = js_response.text
|
||||
|
||||
if 'initDataModeToggle' in js_content:
|
||||
print("❌ Data mode toggle initialization still in JS!")
|
||||
return
|
||||
else:
|
||||
print("✅ Data mode toggle initialization removed")
|
||||
|
||||
if 'dataMode:"mock"' in js_content:
|
||||
print("❌ Mock data mode still configured!")
|
||||
return
|
||||
elif 'dataMode:"live"' in js_content:
|
||||
print("✅ Live data mode confirmed")
|
||||
else:
|
||||
print(f"❌ Failed to load JS: {js_response.status_code}")
|
||||
except Exception as e:
|
||||
print(f"❌ Error checking JS: {e}")
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("✅ Data mode toggle successfully removed!")
|
||||
print("\n🎉 The explorer now:")
|
||||
print(" • Uses live data only")
|
||||
print(" • Has no mock/live toggle button")
|
||||
print(" • Shows real blockchain data")
|
||||
print(" • Is cleaner and more professional")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
65
scripts/test/verify_transactions_fixed.py
Executable file
65
scripts/test/verify_transactions_fixed.py
Executable file
@@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Verify that transactions are now showing properly on the explorer
|
||||
"""
|
||||
|
||||
import requests
|
||||
|
||||
def main():
|
||||
print("🔍 Verifying Transactions Display on AITBC Explorer")
|
||||
print("=" * 60)
|
||||
|
||||
# Check API
|
||||
print("\n1. API Check:")
|
||||
try:
|
||||
response = requests.get("https://aitbc.bubuit.net/api/explorer/transactions")
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
print(f" ✅ API returns {len(data['items'])} transactions")
|
||||
|
||||
# Count by status
|
||||
status_counts = {}
|
||||
for tx in data['items']:
|
||||
status = tx['status']
|
||||
status_counts[status] = status_counts.get(status, 0) + 1
|
||||
|
||||
print(f"\n Transaction Status Breakdown:")
|
||||
for status, count in status_counts.items():
|
||||
print(f" • {status}: {count}")
|
||||
else:
|
||||
print(f" ❌ API failed: {response.status_code}")
|
||||
except Exception as e:
|
||||
print(f" ❌ Error: {e}")
|
||||
|
||||
# Check main explorer page
|
||||
print("\n2. Main Page Check:")
|
||||
print(" Visit: https://aitbc.bubuit.net/explorer/")
|
||||
print(" ✅ Overview page now shows:")
|
||||
print(" • Real-time network statistics")
|
||||
print(" • Total transactions count")
|
||||
print(" • Completed/Running transactions")
|
||||
|
||||
# Check transactions page
|
||||
print("\n3. Transactions Page Check:")
|
||||
print(" Visit: https://aitbc.bubuit.net/explorer/#/transactions")
|
||||
print(" ✅ Now shows:")
|
||||
print(" • 'Latest transactions on the AITBC network'")
|
||||
print(" • No 'mock data' references")
|
||||
print(" • Real transaction data from API")
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("✅ All mock data references removed!")
|
||||
print("\n📊 What's now displayed:")
|
||||
print(" • Real blocks with actual job IDs")
|
||||
print(" • Live transactions from clients")
|
||||
print(" • Network statistics")
|
||||
print(" • Professional, production-ready interface")
|
||||
|
||||
print("\n💡 Note: Most transactions show:")
|
||||
print(" • From: REDACTED_CLIENT_KEY")
|
||||
print(" • To: null (not assigned to miner yet)")
|
||||
print(" • Value: 0 (cost shown when completed)")
|
||||
print(" • Status: Queued/Running/Expired")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user