BEFORE: /opt/aitbc/cli/ ├── aitbc_cli/ # Python package (box in a box) │ ├── commands/ │ ├── main.py │ └── ... ├── setup.py AFTER: /opt/aitbc/cli/ # Flat structure ├── commands/ # Direct access ├── main.py # Direct access ├── auth/ ├── config/ ├── core/ ├── models/ ├── utils/ ├── plugins.py └── setup.py CHANGES MADE: - Moved all files from aitbc_cli/ to cli/ root - Fixed all relative imports (from . to absolute imports) - Updated setup.py entry point: aitbc_cli.main → main - Added CLI directory to Python path in entry script - Simplified deployment.py to remove dependency on deleted core.deployment - Fixed import paths in all command files - Recreated virtual environment with new structure BENEFITS: - Eliminated 'box in a box' nesting - Simpler directory structure - Direct access to all modules - Cleaner imports - Easier maintenance and development - CLI works with both 'python main.py' and 'aitbc' commands
580 lines
25 KiB
Python
Executable File
580 lines
25 KiB
Python
Executable File
"""
|
|
Plugin Marketplace CLI Commands for AITBC
|
|
Commands for browsing, purchasing, and managing plugins from the marketplace
|
|
"""
|
|
|
|
import click
|
|
import json
|
|
import requests
|
|
from datetime import datetime
|
|
from typing import Dict, Any, List, Optional
|
|
|
|
@click.group()
|
|
def plugin_marketplace():
|
|
"""Plugin marketplace commands"""
|
|
pass
|
|
|
|
@plugin_marketplace.command()
|
|
@click.option('--category', help='Filter by category')
|
|
@click.option('--price-min', type=float, help='Minimum price filter')
|
|
@click.option('--price-max', type=float, help='Maximum price filter')
|
|
@click.option('--rating-min', type=float, help='Minimum rating filter')
|
|
@click.option('--sort', default='popularity', help='Sort by (popularity, rating, price, newest)')
|
|
@click.option('--limit', type=int, default=20, help='Number of results')
|
|
@click.option('--test-mode', is_flag=True, help='Run in test mode')
|
|
def browse(category, price_min, price_max, rating_min, sort, limit, test_mode):
|
|
"""Browse plugins in the marketplace"""
|
|
try:
|
|
params = {
|
|
"limit": limit,
|
|
"sort": sort
|
|
}
|
|
|
|
if category:
|
|
params["category"] = category
|
|
if price_min is not None:
|
|
params["price_min"] = price_min
|
|
if price_max is not None:
|
|
params["price_max"] = price_max
|
|
if rating_min is not None:
|
|
params["rating_min"] = rating_min
|
|
|
|
if test_mode:
|
|
# Mock marketplace data
|
|
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",
|
|
"price": 99.99,
|
|
"rating": 4.5,
|
|
"reviews_count": 42,
|
|
"downloads": 1250,
|
|
"featured": True,
|
|
"tags": ["trading", "automation", "bot"],
|
|
"preview_image": "https://marketplace.aitbc.dev/plugins/trading-bot/preview.png"
|
|
},
|
|
{
|
|
"plugin_id": "oracle-feed",
|
|
"name": "Oracle Price Feed",
|
|
"version": "2.1.0",
|
|
"description": "Real-time price oracle integration",
|
|
"author": "Oracle Developer",
|
|
"category": "oracle",
|
|
"price": 49.99,
|
|
"rating": 4.8,
|
|
"reviews_count": 28,
|
|
"downloads": 890,
|
|
"featured": True,
|
|
"tags": ["oracle", "price", "feed"],
|
|
"preview_image": "https://marketplace.aitbc.dev/plugins/oracle-feed/preview.png"
|
|
},
|
|
{
|
|
"plugin_id": "security-scanner",
|
|
"name": "Security Scanner Pro",
|
|
"version": "3.0.0",
|
|
"description": "Advanced security scanning and vulnerability detection",
|
|
"author": "Security Labs",
|
|
"category": "security",
|
|
"price": 199.99,
|
|
"rating": 4.7,
|
|
"reviews_count": 15,
|
|
"downloads": 567,
|
|
"featured": False,
|
|
"tags": ["security", "scanning", "vulnerability"],
|
|
"preview_image": "https://marketplace.aitbc.dev/plugins/security-scanner/preview.png"
|
|
}
|
|
]
|
|
|
|
click.echo("🛒 Plugin Marketplace:")
|
|
click.echo("=" * 60)
|
|
|
|
for plugin in mock_plugins[:limit]:
|
|
featured_badge = "⭐" if plugin.get('featured') else ""
|
|
click.echo(f"{featured_badge} {plugin['name']} (v{plugin['version']})")
|
|
click.echo(f" 💰 Price: ${plugin['price']}")
|
|
click.echo(f" ⭐ Rating: {plugin['rating']}/5.0 ({plugin['reviews_count']} reviews)")
|
|
click.echo(f" 📥 Downloads: {plugin['downloads']}")
|
|
click.echo(f" 📂 Category: {plugin['category']}")
|
|
click.echo(f" 👤 Author: {plugin['author']}")
|
|
click.echo(f" 📝 {plugin['description'][:60]}...")
|
|
click.echo("")
|
|
|
|
return
|
|
|
|
# Fetch from marketplace service
|
|
config = get_config()
|
|
response = requests.get(
|
|
f"{config.coordinator_url}/api/v1/marketplace/browse",
|
|
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("🛒 Plugin Marketplace:")
|
|
click.echo("=" * 60)
|
|
|
|
for plugin in plugins:
|
|
featured_badge = "⭐" if plugin.get('featured') else ""
|
|
click.echo(f"{featured_badge} {plugin['name']} (v{plugin['version']})")
|
|
click.echo(f" 💰 Price: ${plugin.get('price', 0.0)}")
|
|
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" 📂 Category: {plugin.get('category', 'N/A')}")
|
|
click.echo(f" 👤 Author: {plugin.get('author', 'N/A')}")
|
|
click.echo(f" 📝 {plugin['description'][:60]}...")
|
|
click.echo("")
|
|
else:
|
|
click.echo(f"❌ Failed to browse marketplace: {response.text}", err=True)
|
|
|
|
except Exception as e:
|
|
click.echo(f"❌ Error browsing marketplace: {str(e)}", err=True)
|
|
|
|
@plugin_marketplace.command()
|
|
@click.argument('plugin_id')
|
|
@click.option('--test-mode', is_flag=True, help='Run in test mode')
|
|
def details(plugin_id, test_mode):
|
|
"""Get detailed information about a marketplace plugin"""
|
|
try:
|
|
if test_mode:
|
|
# Mock plugin details
|
|
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. Features include real-time market analysis, automated trading strategies, risk management, and portfolio optimization.",
|
|
"author": "AITBC Team",
|
|
"category": "trading",
|
|
"price": 99.99,
|
|
"rating": 4.5,
|
|
"reviews_count": 42,
|
|
"downloads": 1250,
|
|
"featured": True,
|
|
"tags": ["trading", "automation", "bot", "ml", "risk-management"],
|
|
"repository": "https://github.com/aitbc/trading-bot",
|
|
"homepage": "https://aitbc.dev/plugins/trading-bot",
|
|
"license": "MIT",
|
|
"created_at": "2024-01-15T10:30:00Z",
|
|
"updated_at": "2024-03-01T14:20:00Z",
|
|
"preview_image": "https://marketplace.aitbc.dev/plugins/trading-bot/preview.png",
|
|
"screenshots": [
|
|
"https://marketplace.aitbc.dev/plugins/trading-bot/screenshot1.png",
|
|
"https://marketplace.aitbc.dev/plugins/trading-bot/screenshot2.png"
|
|
],
|
|
"documentation": "https://docs.aitbc.dev/plugins/trading-bot",
|
|
"support": "support@aitbc.dev",
|
|
"compatibility": {
|
|
"aitbc_version": ">=1.0.0",
|
|
"python_version": ">=3.8",
|
|
"dependencies": ["exchange-integration", "oracle-feed"]
|
|
},
|
|
"pricing": {
|
|
"type": "one-time",
|
|
"amount": 99.99,
|
|
"currency": "USD",
|
|
"includes_support": True,
|
|
"includes_updates": True
|
|
},
|
|
"reviews": [
|
|
{
|
|
"id": 1,
|
|
"user": "trader123",
|
|
"rating": 5,
|
|
"title": "Excellent trading bot!",
|
|
"comment": "This bot has significantly improved my trading performance. Highly recommended!",
|
|
"date": "2024-02-15T10:30:00Z"
|
|
},
|
|
{
|
|
"id": 2,
|
|
"user": "alice_trader",
|
|
"rating": 4,
|
|
"title": "Good but needs improvements",
|
|
"comment": "Great features but the UI could be more intuitive.",
|
|
"date": "2024-02-10T14:20:00Z"
|
|
}
|
|
]
|
|
}
|
|
|
|
click.echo(f"🛒 Plugin Details: {mock_plugin['name']}")
|
|
click.echo("=" * 60)
|
|
click.echo(f"📦 Version: {mock_plugin['version']}")
|
|
click.echo(f"👤 Author: {mock_plugin['author']}")
|
|
click.echo(f"📂 Category: {mock_plugin['category']}")
|
|
click.echo(f"💰 Price: ${mock_plugin['price']} {mock_plugin['pricing']['currency']}")
|
|
click.echo(f"⭐ Rating: {mock_plugin['rating']}/5.0 ({mock_plugin['reviews_count']} reviews)")
|
|
click.echo(f"📥 Downloads: {mock_plugin['downloads']}")
|
|
click.echo(f"🏷️ Tags: {', '.join(mock_plugin['tags'])}")
|
|
click.echo(f"📄 License: {mock_plugin['license']}")
|
|
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("💰 Pricing:")
|
|
click.echo(f" Type: {mock_plugin['pricing']['type']}")
|
|
click.echo(f" Amount: ${mock_plugin['pricing']['amount']} {mock_plugin['pricing']['currency']}")
|
|
click.echo(f" Includes Support: {'Yes' if mock_plugin['pricing']['includes_support'] else 'No'}")
|
|
click.echo(f" Includes Updates: {'Yes' if mock_plugin['pricing']['includes_updates'] else 'No'}")
|
|
click.echo("")
|
|
click.echo("🔗 Links:")
|
|
click.echo(f" 📦 Repository: {mock_plugin['repository']}")
|
|
click.echo(f" 🌐 Homepage: {mock_plugin['homepage']}")
|
|
click.echo(f" 📚 Documentation: {mock_plugin['documentation']}")
|
|
click.echo(f" 📧 Support: {mock_plugin['support']}")
|
|
click.echo("")
|
|
click.echo("🔧 Compatibility:")
|
|
click.echo(f" AITBC Version: {mock_plugin['compatibility']['aitbc_version']}")
|
|
click.echo(f" Python Version: {mock_plugin['compatibility']['python_version']}")
|
|
click.echo(f" Dependencies: {', '.join(mock_plugin['compatibility']['dependencies'])}")
|
|
click.echo("")
|
|
click.echo("⭐ Recent Reviews:")
|
|
for review in mock_plugin['reviews'][:3]:
|
|
stars = "⭐" * review['rating']
|
|
click.echo(f" {stars} {review['title']}")
|
|
click.echo(f" 👤 {review['user']} - {review['date']}")
|
|
click.echo(f" 📝 {review['comment']}")
|
|
click.echo("")
|
|
return
|
|
|
|
# Fetch from marketplace service
|
|
config = get_config()
|
|
response = requests.get(
|
|
f"{config.coordinator_url}/api/v1/marketplace/plugins/{plugin_id}",
|
|
headers={"Authorization": f"Bearer {config.api_key}"},
|
|
timeout=30
|
|
)
|
|
|
|
if response.status_code == 200:
|
|
plugin = response.json()
|
|
|
|
click.echo(f"🛒 Plugin Details: {plugin['name']}")
|
|
click.echo("=" * 60)
|
|
click.echo(f"📦 Version: {plugin['version']}")
|
|
click.echo(f"👤 Author: {plugin['author']}")
|
|
click.echo(f"📂 Category: {plugin['category']}")
|
|
click.echo(f"💰 Price: ${plugin.get('price', 0.0)}")
|
|
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"🏷️ Tags: {', '.join(plugin.get('tags', []))}")
|
|
click.echo(f"📄 License: {plugin.get('license', 'N/A')}")
|
|
click.echo(f"📅 Created: {plugin['created_at']}")
|
|
click.echo(f"🔄 Updated: {plugin['updated_at']}")
|
|
click.echo("")
|
|
click.echo("📝 Description:")
|
|
click.echo(f" {plugin['description']}")
|
|
else:
|
|
click.echo(f"❌ Plugin not found: {response.text}", err=True)
|
|
|
|
except Exception as e:
|
|
click.echo(f"❌ Error getting plugin details: {str(e)}", err=True)
|
|
|
|
@plugin_marketplace.command()
|
|
@click.argument('plugin_id')
|
|
@click.option('--test-mode', is_flag=True, help='Run in test mode')
|
|
def purchase(plugin_id, test_mode):
|
|
"""Purchase a plugin from the marketplace"""
|
|
try:
|
|
if test_mode:
|
|
click.echo(f"💰 Purchase initiated (test mode)")
|
|
click.echo(f"📦 Plugin ID: {plugin_id}")
|
|
click.echo(f"💳 Payment method: Test Card")
|
|
click.echo(f"💰 Amount: $99.99")
|
|
click.echo(f"✅ Purchase completed successfully")
|
|
click.echo(f"📧 License key: TEST-KEY-{plugin_id.upper()}")
|
|
click.echo(f"📥 Download link: https://marketplace.aitbc.dev/download/{plugin_id}")
|
|
return
|
|
|
|
# Get plugin details first
|
|
config = get_config()
|
|
response = requests.get(
|
|
f"{config.coordinator_url}/api/v1/marketplace/plugins/{plugin_id}",
|
|
headers={"Authorization": f"Bearer {config.api_key}"},
|
|
timeout=30
|
|
)
|
|
|
|
if response.status_code != 200:
|
|
click.echo(f"❌ Plugin not found: {response.text}", err=True)
|
|
return
|
|
|
|
plugin = response.json()
|
|
|
|
# Create purchase order
|
|
purchase_data = {
|
|
"plugin_id": plugin_id,
|
|
"price": plugin.get('price', 0.0),
|
|
"currency": plugin.get('pricing', {}).get('currency', 'USD'),
|
|
"payment_method": "credit_card",
|
|
"purchased_at": datetime.utcnow().isoformat()
|
|
}
|
|
|
|
response = requests.post(
|
|
f"{config.coordinator_url}/api/v1/marketplace/purchase",
|
|
json=purchase_data,
|
|
headers={"Authorization": f"Bearer {config.api_key}"},
|
|
timeout=30
|
|
)
|
|
|
|
if response.status_code == 201:
|
|
result = response.json()
|
|
click.echo(f"💰 Purchase completed successfully!")
|
|
click.echo(f"📦 Plugin: {result['plugin_name']}")
|
|
click.echo(f"💳 Amount: ${result['amount']} {result['currency']}")
|
|
click.echo(f"📧 License Key: {result['license_key']}")
|
|
click.echo(f"📥 Download: {result['download_url']}")
|
|
click.echo(f"📧 Support: {result['support_email']}")
|
|
else:
|
|
click.echo(f"❌ Purchase failed: {response.text}", err=True)
|
|
|
|
except Exception as e:
|
|
click.echo(f"❌ Error purchasing plugin: {str(e)}", err=True)
|
|
|
|
@plugin_marketplace.command()
|
|
@click.option('--category', help='Filter by category')
|
|
@click.option('--price-min', type=float, help='Minimum price filter')
|
|
@click.option('--price-max', type=float, help='Maximum price filter')
|
|
@click.option('--rating-min', type=float, help='Minimum rating filter')
|
|
@click.option('--limit', type=int, default=10, help='Number of results')
|
|
@click.option('--test-mode', is_flag=True, help='Run in test mode')
|
|
def featured(category, price_min, price_max, rating_min, limit, test_mode):
|
|
"""Browse featured plugins"""
|
|
try:
|
|
params = {
|
|
"featured": True,
|
|
"limit": limit
|
|
}
|
|
|
|
if category:
|
|
params["category"] = category
|
|
if price_min is not None:
|
|
params["price_min"] = price_min
|
|
if price_max is not None:
|
|
params["price_max"] = price_max
|
|
if rating_min is not None:
|
|
params["rating_min"] = rating_min
|
|
|
|
if test_mode:
|
|
# Mock featured plugins
|
|
mock_featured = [
|
|
{
|
|
"plugin_id": "trading-bot",
|
|
"name": "Advanced Trading Bot",
|
|
"version": "1.0.0",
|
|
"description": "Automated trading bot with advanced algorithms",
|
|
"author": "AITBC Team",
|
|
"category": "trading",
|
|
"price": 99.99,
|
|
"rating": 4.5,
|
|
"downloads": 1250,
|
|
"featured": True,
|
|
"featured_reason": "Top-rated trading automation tool"
|
|
},
|
|
{
|
|
"plugin_id": "oracle-feed",
|
|
"name": "Oracle Price Feed",
|
|
"version": "2.1.0",
|
|
"description": "Real-time price oracle integration",
|
|
"author": "Oracle Developer",
|
|
"category": "oracle",
|
|
"price": 49.99,
|
|
"rating": 4.8,
|
|
"downloads": 890,
|
|
"featured": True,
|
|
"featured_reason": "Most reliable oracle integration"
|
|
}
|
|
]
|
|
|
|
click.echo("⭐ Featured Plugins:")
|
|
click.echo("=" * 60)
|
|
|
|
for plugin in mock_featured[:limit]:
|
|
click.echo(f"⭐ {plugin['name']} (v{plugin['version']})")
|
|
click.echo(f" 💰 Price: ${plugin['price']}")
|
|
click.echo(f" ⭐ Rating: {plugin['rating']}/5.0")
|
|
click.echo(f" 📥 Downloads: {plugin['downloads']}")
|
|
click.echo(f" 📂 Category: {plugin['category']}")
|
|
click.echo(f" 👤 Author: {plugin['author']}")
|
|
click.echo(f" 🏆 {plugin['featured_reason']}")
|
|
click.echo("")
|
|
|
|
return
|
|
|
|
# Fetch from marketplace service
|
|
config = get_config()
|
|
response = requests.get(
|
|
f"{config.coordinator_url}/api/v1/marketplace/featured",
|
|
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("⭐ Featured Plugins:")
|
|
click.echo("=" * 60)
|
|
|
|
for plugin in plugins:
|
|
click.echo(f"⭐ {plugin['name']} (v{plugin['version']})")
|
|
click.echo(f" 💰 Price: ${plugin.get('price', 0.0)}")
|
|
click.echo(f" ⭐ Rating: {plugin.get('rating', 0)}/5.0")
|
|
click.echo(f" 📥 Downloads: {plugin.get('downloads', 0)}")
|
|
click.echo(f" 📂 Category: {plugin.get('category', 'N/A')}")
|
|
click.echo(f" 👤 Author: {plugin.get('author', 'N/A')}")
|
|
click.echo(f" 🏆 {plugin.get('featured_reason', 'Featured plugin')}")
|
|
click.echo("")
|
|
else:
|
|
click.echo(f"❌ Failed to get featured plugins: {response.text}", err=True)
|
|
|
|
except Exception as e:
|
|
click.echo(f"❌ Error getting featured plugins: {str(e)}", err=True)
|
|
|
|
@plugin_marketplace.command()
|
|
@click.option('--test-mode', is_flag=True, help='Run in test mode')
|
|
def my_purchases(test_mode):
|
|
"""View your purchased plugins"""
|
|
try:
|
|
if test_mode:
|
|
# Mock purchase history
|
|
mock_purchases = [
|
|
{
|
|
"plugin_id": "trading-bot",
|
|
"name": "Advanced Trading Bot",
|
|
"version": "1.0.0",
|
|
"purchase_date": "2024-02-15T10:30:00Z",
|
|
"price": 99.99,
|
|
"license_key": "TEST-KEY-TRADING-BOT",
|
|
"status": "active",
|
|
"download_count": 5
|
|
},
|
|
{
|
|
"plugin_id": "oracle-feed",
|
|
"name": "Oracle Price Feed",
|
|
"version": "2.1.0",
|
|
"purchase_date": "2024-02-10T14:20:00Z",
|
|
"price": 49.99,
|
|
"license_key": "TEST-KEY-ORACLE-FEED",
|
|
"status": "active",
|
|
"download_count": 3
|
|
}
|
|
]
|
|
|
|
click.echo("📋 Your Purchased Plugins:")
|
|
click.echo("=" * 60)
|
|
|
|
for purchase in mock_purchases:
|
|
status_icon = "✅" if purchase['status'] == 'active' else "⏳"
|
|
click.echo(f"{status_icon} {purchase['name']} (v{purchase['version']})")
|
|
click.echo(f" 📅 Purchased: {purchase['purchase_date']}")
|
|
click.echo(f" 💰 Price: ${purchase['price']}")
|
|
click.echo(f" 📧 License Key: {purchase['license_key']}")
|
|
click.echo(f" 📥 Downloads: {purchase['download_count']}")
|
|
click.echo("")
|
|
|
|
return
|
|
|
|
# Get user's purchases
|
|
config = get_config()
|
|
response = requests.get(
|
|
f"{config.coordinator_url}/api/v1/marketplace/purchases",
|
|
headers={"Authorization": f"Bearer {config.api_key}"},
|
|
timeout=30
|
|
)
|
|
|
|
if response.status_code == 200:
|
|
result = response.json()
|
|
purchases = result.get("purchases", [])
|
|
|
|
click.echo("📋 Your Purchased Plugins:")
|
|
click.echo("=" * 60)
|
|
|
|
for purchase in purchases:
|
|
status_icon = "✅" if purchase['status'] == 'active' else "⏳"
|
|
click.echo(f"{status_icon} {purchase['plugin_name']} (v{purchase['version']})")
|
|
click.echo(f" 📅 Purchased: {purchase['purchase_date']}")
|
|
click.echo(f" 💰 Price: ${purchase['price']} {purchase['currency']}")
|
|
click.echo(f" 📧 License Key: {purchase['license_key']}")
|
|
click.echo(f" 📥 Downloads: {purchase.get('download_count', 0)}")
|
|
click.echo("")
|
|
else:
|
|
click.echo(f"❌ Failed to get purchases: {response.text}", err=True)
|
|
|
|
except Exception as e:
|
|
click.echo(f"❌ Error getting purchases: {str(e)}", err=True)
|
|
|
|
@plugin_marketplace.command()
|
|
@click.argument('plugin_id')
|
|
@click.option('--license-key', help='License key for the plugin')
|
|
@click.option('--test-mode', is_flag=True, help='Run in test mode')
|
|
def download(plugin_id, license_key, test_mode):
|
|
"""Download a purchased plugin"""
|
|
try:
|
|
if test_mode:
|
|
click.echo(f"📥 Download started (test mode)")
|
|
click.echo(f"📦 Plugin ID: {plugin_id}")
|
|
click.echo(f"📧 License Key: {license_key or 'TEST-KEY'}")
|
|
click.echo(f"✅ Download completed successfully")
|
|
click.echo(f"📁 Download location: /tmp/{plugin_id}.zip")
|
|
return
|
|
|
|
# Validate license key
|
|
config = get_config()
|
|
response = requests.post(
|
|
f"{config.coordinator_url}/api/v1/marketplace/download/{plugin_id}",
|
|
json={"license_key": license_key},
|
|
headers={"Authorization": f"Bearer {config.api_key}"},
|
|
timeout=30
|
|
)
|
|
|
|
if response.status_code == 200:
|
|
result = response.json()
|
|
click.echo(f"📥 Download started!")
|
|
click.echo(f"📦 Plugin: {result['plugin_name']}")
|
|
click.echo(f"📁 Download URL: {result['download_url']}")
|
|
click.echo(f"📦 File Size: {result['file_size_mb']} MB")
|
|
click.echo(f"🔑 Checksum: {result['checksum']}")
|
|
|
|
# Download the file
|
|
download_response = requests.get(result['download_url'], timeout=60)
|
|
|
|
if download_response.status_code == 200:
|
|
filename = f"{plugin_id}.zip"
|
|
with open(filename, 'wb') as f:
|
|
f.write(download_response.content)
|
|
|
|
click.echo(f"✅ Download completed!")
|
|
click.echo(f"📁 Saved as: {filename}")
|
|
click.echo(f"📁 Size: {len(download_response.content) / 1024 / 1024:.1f} MB")
|
|
else:
|
|
click.echo(f"❌ Download failed: {download_response.text}", err=True)
|
|
else:
|
|
click.echo(f"❌ Download failed: {response.text}", err=True)
|
|
|
|
except Exception as e:
|
|
click.echo(f"❌ Error downloading plugin: {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:8014",
|
|
api_key="test-api-key"
|
|
)
|
|
|
|
if __name__ == "__main__":
|
|
plugin_marketplace()
|