Files
aitbc/cli/commands/plugin_registry.py
aitbc d26e6d3772
Some checks failed
API Endpoint Tests / test-api-endpoints (push) Successful in 22s
Blockchain Synchronization Verification / sync-verification (push) Successful in 3s
CLI Tests / test-cli (push) Failing after 13s
Cross-Chain Functionality Tests / test-cross-chain-sync (push) Failing after 3s
Cross-Chain Functionality Tests / test-cross-chain-transactions (push) Successful in 3s
Cross-Chain Functionality Tests / test-cross-chain-bridge (push) Has been skipped
Cross-Chain Functionality Tests / test-multi-chain-consensus (push) Failing after 3s
Cross-Chain Functionality Tests / aggregate-results (push) Has been skipped
Cross-Node Transaction Testing / transaction-test (push) Successful in 2s
Deploy to Testnet / deploy-testnet (push) Successful in 1m34s
Documentation Validation / validate-docs (push) Failing after 10s
Documentation Validation / validate-policies-strict (push) Successful in 3s
Multi-Node Stress Testing / stress-test (push) Has been cancelled
Node Failover Simulation / failover-test (push) Has been cancelled
Python Tests / test-python (push) Has been cancelled
Integration Tests / test-service-integration (push) Successful in 2m42s
Multi-Chain Island Architecture Tests / test-multi-chain-island (push) Successful in 3s
Multi-Node Blockchain Health Monitoring / health-check (push) Successful in 5s
P2P Network Verification / p2p-verification (push) Successful in 3s
Package Tests / Python package - aitbc-agent-sdk (push) Failing after 33s
Package Tests / Python package - aitbc-core (push) Successful in 17s
Package Tests / Python package - aitbc-crypto (push) Successful in 11s
Security Scanning / security-scan (push) Has been cancelled
Package Tests / Python package - aitbc-sdk (push) Successful in 13s
Package Tests / JavaScript package - aitbc-sdk-js (push) Successful in 9s
Package Tests / JavaScript package - aitbc-token (push) Successful in 17s
Staking Tests / test-staking-service (push) Failing after 6s
Staking Tests / test-staking-integration (push) Has been skipped
Staking Tests / test-staking-contract (push) Has been skipped
Staking Tests / run-staking-test-runner (push) Has been skipped
fix: replace datetime.UTC with timezone.utc for Python 3.12+ compatibility
2026-05-09 12:03:26 +02:00

504 lines
21 KiB
Python
Executable File

"""
Plugin Registry CLI Commands for AITBC
Commands for managing plugin registration, versioning, and discovery
"""
import click
import json
import requests
from datetime import datetime, timezone
from pathlib import Path
from typing import Dict, Any, List, Optional
@click.group()
def plugin_registry():
"""Plugin registry management commands"""
pass
@plugin_registry.command()
@click.option('--plugin-id', help='Plugin ID to register')
@click.option('--name', required=True, help='Plugin name')
@click.option('--version', required=True, help='Plugin version')
@click.option('--description', required=True, help='Plugin description')
@click.option('--author', required=True, help='Plugin author')
@click.option('--category', required=True, help='Plugin category')
@click.option('--tags', help='Plugin tags (comma-separated)')
@click.option('--repository', help='Source repository URL')
@click.option('--homepage', help='Plugin homepage URL')
@click.option('--license', help='Plugin license')
@click.option('--test-mode', is_flag=True, help='Run in test mode')
def register(plugin_id, name, version, description, author, category, tags, repository, homepage, license, test_mode):
"""Register a new plugin in the registry"""
try:
if not plugin_id:
plugin_id = name.lower().replace(' ', '-').replace('_', '-')
# Create plugin registration data
plugin_data = {
"plugin_id": plugin_id,
"name": name,
"version": version,
"description": description,
"author": author,
"category": category,
"tags": tags.split(',') if tags else [],
"repository": repository,
"homepage": homepage,
"license": license,
"status": "active",
"created_at": datetime.now(timezone.utc).isoformat(),
"updated_at": datetime.now(timezone.utc).isoformat(),
"downloads": 0,
"rating": 0.0,
"reviews_count": 0
}
if test_mode:
# Mock registration for testing
plugin_data["registration_id"] = f"reg_{int(datetime.now(timezone.utc).timestamp())}"
plugin_data["status"] = "registered"
click.echo(f"✅ Plugin registered successfully (test mode)")
click.echo(f"📋 Plugin ID: {plugin_data['plugin_id']}")
click.echo(f"📦 Version: {plugin_data['version']}")
click.echo(f"📝 Description: {plugin_data['description']}")
return
# Send to registry service
config = get_config()
response = requests.post(
f"{config.coordinator_url}/api/v1/plugins/register",
json=plugin_data,
headers={"Authorization": f"Bearer {config.api_key}"},
timeout=30
)
if response.status_code == 201:
result = response.json()
click.echo(f"✅ Plugin registered successfully")
click.echo(f"📋 Plugin ID: {result['plugin_id']}")
click.echo(f"📦 Version: {result['version']}")
click.echo(f"📝 Description: {result['description']}")
else:
click.echo(f"❌ Registration failed: {response.text}", err=True)
except Exception as e:
click.echo(f"❌ Error registering plugin: {str(e)}", err=True)
@plugin_registry.command()
@click.option('--plugin-id', help='Specific plugin ID (optional)')
@click.option('--category', help='Filter by category')
@click.option('--author', help='Filter by author')
@click.option('--status', help='Filter by status')
@click.option('--limit', type=int, default=20, help='Number of results to return')
@click.option('--test-mode', is_flag=True, help='Run in test mode')
def list(plugin_id, category, author, status, limit, test_mode):
"""List registered plugins"""
try:
if test_mode:
# Mock data for testing
mock_plugins = [
{
"plugin_id": "trading-bot",
"name": "Advanced Trading Bot",
"version": "1.0.0",
"description": "Automated trading bot with advanced algorithms",
"author": "AITBC Team",
"category": "trading",
"tags": ["trading", "automation", "bot"],
"status": "active",
"downloads": 1250,
"rating": 4.5,
"reviews_count": 42
},
{
"plugin_id": "oracle-feed",
"name": "Oracle Price Feed",
"version": "2.1.0",
"description": "Real-time price oracle integration",
"author": "Oracle Developer",
"category": "oracle",
"tags": ["oracle", "price", "feed"],
"status": "active",
"downloads": 890,
"rating": 4.8,
"reviews_count": 28
}
]
click.echo("📋 Registered Plugins:")
click.echo("=" * 60)
for plugin in mock_plugins[:limit]:
click.echo(f"📦 {plugin['name']} (v{plugin['version']})")
click.echo(f" 🆔 ID: {plugin['plugin_id']}")
click.echo(f" 👤 Author: {plugin['author']}")
click.echo(f" 📂 Category: {plugin['category']}")
click.echo(f" ⭐ Rating: {plugin['rating']}/5.0 ({plugin['reviews_count']} reviews)")
click.echo(f" 📥 Downloads: {plugin['downloads']}")
click.echo(f" 📝 {plugin['description'][:60]}...")
click.echo("")
return
# Fetch from registry service
config = get_config()
params = {
"limit": limit
}
if plugin_id:
params["plugin_id"] = plugin_id
if category:
params["category"] = category
if author:
params["author"] = author
if status:
params["status"] = status
response = requests.get(
f"{config.coordinator_url}/api/v1/plugins",
params=params,
headers={"Authorization": f"Bearer {config.api_key}"},
timeout=30
)
if response.status_code == 200:
result = response.json()
plugins = result.get("plugins", [])
click.echo("📋 Registered Plugins:")
click.echo("=" * 60)
for plugin in plugins:
click.echo(f"📦 {plugin['name']} (v{plugin['version']})")
click.echo(f" 🆔 ID: {plugin['plugin_id']}")
click.echo(f" 👤 Author: {plugin['author']}")
click.echo(f" 📂 Category: {plugin['category']}")
click.echo(f" ⭐ Rating: {plugin.get('rating', 0)}/5.0 ({plugin.get('reviews_count', 0)} reviews)")
click.echo(f" 📥 Downloads: {plugin.get('downloads', 0)}")
click.echo(f" 📝 {plugin['description'][:60]}...")
click.echo("")
else:
click.echo(f"❌ Failed to list plugins: {response.text}", err=True)
except Exception as e:
click.echo(f"❌ Error listing plugins: {str(e)}", err=True)
@plugin_registry.command()
@click.argument('plugin_id')
@click.option('--test-mode', is_flag=True, help='Run in test mode')
def info(plugin_id, test_mode):
"""Get detailed information about a specific plugin"""
try:
if test_mode:
# Mock data for testing
mock_plugin = {
"plugin_id": plugin_id,
"name": "Advanced Trading Bot",
"version": "1.0.0",
"description": "Automated trading bot with advanced algorithms and machine learning capabilities",
"author": "AITBC Team",
"category": "trading",
"tags": ["trading", "automation", "bot", "ml"],
"repository": "https://github.com/aitbc/trading-bot",
"homepage": "https://aitbc.dev/plugins/trading-bot",
"license": "MIT",
"status": "active",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-03-01T14:20:00Z",
"downloads": 1250,
"rating": 4.5,
"reviews_count": 42,
"dependencies": ["exchange-integration", "oracle-feed"],
"security_scan": {
"status": "passed",
"scan_date": "2024-03-01T14:20:00Z",
"vulnerabilities": 0
},
"performance_metrics": {
"cpu_usage": 2.5,
"memory_usage": 512,
"response_time_ms": 45
}
}
click.echo(f"📦 Plugin Information: {mock_plugin['name']}")
click.echo("=" * 60)
click.echo(f"🆔 Plugin ID: {mock_plugin['plugin_id']}")
click.echo(f"📦 Version: {mock_plugin['version']}")
click.echo(f"👤 Author: {mock_plugin['author']}")
click.echo(f"📂 Category: {mock_plugin['category']}")
click.echo(f"🏷️ Tags: {', '.join(mock_plugin['tags'])}")
click.echo(f"📄 License: {mock_plugin['license']}")
click.echo(f"📊 Status: {mock_plugin['status']}")
click.echo(f"⭐ Rating: {mock_plugin['rating']}/5.0 ({mock_plugin['reviews_count']} reviews)")
click.echo(f"📥 Downloads: {mock_plugin['downloads']}")
click.echo(f"📅 Created: {mock_plugin['created_at']}")
click.echo(f"🔄 Updated: {mock_plugin['updated_at']}")
click.echo("")
click.echo("📝 Description:")
click.echo(f" {mock_plugin['description']}")
click.echo("")
click.echo("🔗 Links:")
click.echo(f" 📦 Repository: {mock_plugin['repository']}")
click.echo(f" 🌐 Homepage: {mock_plugin['homepage']}")
click.echo("")
click.echo("🔒 Security Scan:")
click.echo(f" Status: {mock_plugin['security_scan']['status']}")
click.echo(f" Scan Date: {mock_plugin['security_scan']['scan_date']}")
click.echo(f" Vulnerabilities: {mock_plugin['security_scan']['vulnerabilities']}")
click.echo("")
click.echo("⚡ Performance Metrics:")
click.echo(f" CPU Usage: {mock_plugin['performance_metrics']['cpu_usage']}%")
click.echo(f" Memory Usage: {mock_plugin['performance_metrics']['memory_usage']}MB")
click.echo(f" Response Time: {mock_plugin['performance_metrics']['response_time_ms']}ms")
return
# Fetch from registry service
config = get_config()
response = requests.get(
f"{config.coordinator_url}/api/v1/plugins/{plugin_id}",
headers={"Authorization": f"Bearer {config.api_key}"},
timeout=30
)
if response.status_code == 200:
plugin = response.json()
click.echo(f"📦 Plugin Information: {plugin['name']}")
click.echo("=" * 60)
click.echo(f"🆔 Plugin ID: {plugin['plugin_id']}")
click.echo(f"📦 Version: {plugin['version']}")
click.echo(f"👤 Author: {plugin['author']}")
click.echo(f"📂 Category: {plugin['category']}")
click.echo(f"🏷️ Tags: {', '.join(plugin.get('tags', []))}")
click.echo(f"📄 License: {plugin.get('license', 'N/A')}")
click.echo(f"📊 Status: {plugin['status']}")
click.echo(f"⭐ Rating: {plugin.get('rating', 0)}/5.0 ({plugin.get('reviews_count', 0)} reviews)")
click.echo(f"📥 Downloads: {plugin.get('downloads', 0)}")
click.echo(f"📅 Created: {plugin['created_at']}")
click.echo(f"🔄 Updated: {plugin['updated_at']}")
click.echo("")
click.echo("📝 Description:")
click.echo(f" {plugin['description']}")
click.echo("")
if plugin.get('repository'):
click.echo("🔗 Links:")
click.echo(f" 📦 Repository: {plugin['repository']}")
if plugin.get('homepage'):
click.echo(f" 🌐 Homepage: {plugin['homepage']}")
else:
click.echo(f"❌ Plugin not found: {response.text}", err=True)
except Exception as e:
click.echo(f"❌ Error getting plugin info: {str(e)}", err=True)
@plugin_registry.command()
@click.argument('plugin_id')
@click.option('--version', required=True, help='New version number')
@click.option('--changelog', required=True, help='Version changelog')
@click.option('--test-mode', is_flag=True, help='Run in test mode')
def update_version(plugin_id, version, changelog, test_mode):
"""Update plugin version"""
try:
update_data = {
"version": version,
"changelog": changelog,
"updated_at": datetime.now(timezone.utc).isoformat()
}
if test_mode:
click.echo(f"✅ Plugin version updated (test mode)")
click.echo(f"📦 Plugin ID: {plugin_id}")
click.echo(f"📦 New Version: {version}")
click.echo(f"📝 Changelog: {changelog}")
return
# Send to registry service
config = get_config()
response = requests.put(
f"{config.coordinator_url}/api/v1/plugins/{plugin_id}/version",
json=update_data,
headers={"Authorization": f"Bearer {config.api_key}"},
timeout=30
)
if response.status_code == 200:
result = response.json()
click.echo(f"✅ Plugin version updated successfully")
click.echo(f"📦 Plugin ID: {result['plugin_id']}")
click.echo(f"📦 New Version: {result['version']}")
click.echo(f"📝 Changelog: {changelog}")
else:
click.echo(f"❌ Version update failed: {response.text}", err=True)
except Exception as e:
click.echo(f"❌ Error updating plugin version: {str(e)}", err=True)
@plugin_registry.command()
@click.option('--query', help='Search query')
@click.option('--category', help='Filter by category')
@click.option('--tags', help='Filter by tags (comma-separated)')
@click.option('--limit', type=int, default=10, help='Number of results')
@click.option('--test-mode', is_flag=True, help='Run in test mode')
def search(query, category, tags, limit, test_mode):
"""Search for plugins"""
try:
search_params = {
"limit": limit
}
if query:
search_params["query"] = query
if category:
search_params["category"] = category
if tags:
search_params["tags"] = tags.split(',')
if test_mode:
# Mock search results
mock_results = [
{
"plugin_id": "trading-bot",
"name": "Advanced Trading Bot",
"version": "1.0.0",
"description": "Automated trading bot with advanced algorithms",
"relevance_score": 0.95
},
{
"plugin_id": "oracle-feed",
"name": "Oracle Price Feed",
"version": "2.1.0",
"description": "Real-time price oracle integration",
"relevance_score": 0.87
}
]
click.echo(f"🔍 Search Results for '{query or 'all'}':")
click.echo("=" * 60)
for result in mock_results:
click.echo(f"📦 {result['name']} (v{result['version']})")
click.echo(f" 🆔 ID: {result['plugin_id']}")
click.echo(f" 📝 {result['description'][:60]}...")
click.echo(f" 📊 Relevance: {result['relevance_score']:.2f}")
click.echo("")
return
# Search in registry service
config = get_config()
response = requests.get(
f"{config.coordinator_url}/api/v1/plugins/search",
params=search_params,
headers={"Authorization": f"Bearer {config.api_key}"},
timeout=30
)
if response.status_code == 200:
result = response.json()
plugins = result.get("plugins", [])
click.echo(f"🔍 Search Results for '{query or 'all'}':")
click.echo("=" * 60)
for plugin in plugins:
click.echo(f"📦 {plugin['name']} (v{plugin['version']})")
click.echo(f" 🆔 ID: {plugin['plugin_id']}")
click.echo(f" 📝 {plugin['description'][:60]}...")
click.echo(f" 📊 Relevance: {plugin.get('relevance_score', 0):.2f}")
click.echo("")
else:
click.echo(f"❌ Search failed: {response.text}", err=True)
except Exception as e:
click.echo(f"❌ Error searching plugins: {str(e)}", err=True)
@plugin_registry.command()
@click.option('--test-mode', is_flag=True, help='Run in test mode')
def status(test_mode):
"""Get plugin registry status"""
try:
if test_mode:
# Mock status data
status_data = {
"total_plugins": 156,
"active_plugins": 142,
"pending_plugins": 8,
"inactive_plugins": 6,
"total_downloads": 45678,
"categories": {
"trading": 45,
"oracle": 32,
"security": 28,
"analytics": 25,
"utility": 26
},
"recent_registrations": 12,
"security_scans": {
"passed": 148,
"failed": 3,
"pending": 5
}
}
click.echo("📊 Plugin Registry Status:")
click.echo("=" * 40)
click.echo(f"📦 Total Plugins: {status_data['total_plugins']}")
click.echo(f"✅ Active Plugins: {status_data['active_plugins']}")
click.echo(f"⏳ Pending Plugins: {status_data['pending_plugins']}")
click.echo(f"❌ Inactive Plugins: {status_data['inactive_plugins']}")
click.echo(f"📥 Total Downloads: {status_data['total_downloads']}")
click.echo("")
click.echo("📂 Categories:")
for category, count in status_data['categories'].items():
click.echo(f" {category}: {count}")
click.echo("")
click.echo("🔒 Security Scans:")
click.echo(f" ✅ Passed: {status_data['security_scans']['passed']}")
click.echo(f" ❌ Failed: {status_data['security_scans']['failed']}")
click.echo(f" ⏳ Pending: {status_data['security_scans']['pending']}")
return
# Get status from registry service
config = get_config()
response = requests.get(
f"{config.coordinator_url}/api/v1/plugins/status",
headers={"Authorization": f"Bearer {config.api_key}"},
timeout=30
)
if response.status_code == 200:
status = response.json()
click.echo("📊 Plugin Registry Status:")
click.echo("=" * 40)
click.echo(f"📦 Total Plugins: {status.get('total_plugins', 0)}")
click.echo(f"✅ Active Plugins: {status.get('active_plugins', 0)}")
click.echo(f"⏳ Pending Plugins: {status.get('pending_plugins', 0)}")
click.echo(f"❌ Inactive Plugins: {status.get('inactive_plugins', 0)}")
click.echo(f"📥 Total Downloads: {status.get('total_downloads', 0)}")
click.echo(f"📈 Recent Registrations: {status.get('recent_registrations', 0)}")
else:
click.echo(f"❌ Failed to get status: {response.text}", err=True)
except Exception as e:
click.echo(f"❌ Error getting status: {str(e)}", err=True)
# Helper function to get config
def get_config():
"""Get CLI configuration"""
try:
from config import get_config
return get_config()
except ImportError:
# Fallback for testing
from types import SimpleNamespace
return SimpleNamespace(
coordinator_url="http://localhost:8013",
api_key="test-api-key"
)
if __name__ == "__main__":
plugin_registry()