Add missing plugin CLI commands and REST API endpoint
Some checks failed
Cross-Node Transaction Testing / transaction-test (push) Has been cancelled
Deploy to Testnet / deploy-testnet (push) Has been cancelled
Integration Tests / test-service-integration (push) Has been cancelled
Multi-Node Stress Testing / stress-test (push) Has been cancelled
Python Tests / test-python (push) Has been cancelled
Security Scanning / security-scan (push) Has been cancelled
CLI Tests / test-cli (push) Has been cancelled
Some checks failed
Cross-Node Transaction Testing / transaction-test (push) Has been cancelled
Deploy to Testnet / deploy-testnet (push) Has been cancelled
Integration Tests / test-service-integration (push) Has been cancelled
Multi-Node Stress Testing / stress-test (push) Has been cancelled
Python Tests / test-python (push) Has been cancelled
Security Scanning / security-scan (push) Has been cancelled
CLI Tests / test-cli (push) Has been cancelled
CLI: - Added 'aitbc plugin create' - Create new plugin skeleton - Added 'aitbc plugin package' - Package plugin for distribution - Added 'aitbc market list-plugin' - List marketplace plugins Marketplace service: - Added GET /v1/marketplace/plugins endpoint - Returns mock plugin data (cli_enhancer, web_dashboard, security_scanner) Fixes scenario doc references to non-existent plugin commands
This commit is contained in:
@@ -246,6 +246,49 @@ async def get_analytics(
|
||||
return await svc.get_analytics(period_type=period_type)
|
||||
|
||||
|
||||
@app.get("/v1/marketplace/plugins")
|
||||
async def get_plugins(
|
||||
type: str | None = None,
|
||||
svc: MarketplaceService = Depends(get_marketplace_service),
|
||||
):
|
||||
"""Get marketplace plugins"""
|
||||
try:
|
||||
logger.info(f"GET /v1/marketplace/plugins called with type={type}")
|
||||
# Return mock plugin data for now
|
||||
plugins = [
|
||||
{
|
||||
"id": "plugin_001",
|
||||
"name": "cli_enhancer",
|
||||
"type": "cli",
|
||||
"author": "AITBC Team",
|
||||
"description": "Enhances CLI with additional commands",
|
||||
"version": "1.0.0"
|
||||
},
|
||||
{
|
||||
"id": "plugin_002",
|
||||
"name": "web_dashboard",
|
||||
"type": "web",
|
||||
"author": "AITBC Team",
|
||||
"description": "Web dashboard plugin for monitoring",
|
||||
"version": "1.2.0"
|
||||
},
|
||||
{
|
||||
"id": "plugin_003",
|
||||
"name": "security_scanner",
|
||||
"type": "blockchain",
|
||||
"author": "Security Team",
|
||||
"description": "Security scanning plugin for blockchain",
|
||||
"version": "0.9.0"
|
||||
}
|
||||
]
|
||||
if type:
|
||||
plugins = [p for p in plugins if p.get("type") == type]
|
||||
return {"plugins": plugins}
|
||||
except Exception as e:
|
||||
logger.error(f"Error in GET /v1/marketplace/plugins: {type(e).__name__}: {str(e)}")
|
||||
raise
|
||||
|
||||
|
||||
@app.post("/v1/transactions")
|
||||
async def submit_transaction(transaction_data: dict, session: AsyncSession = Depends(get_session_dep)):
|
||||
"""Submit marketplace transaction"""
|
||||
|
||||
@@ -154,6 +154,122 @@ def uninstall(ctx, name: str):
|
||||
output({"name": name, "status": "uninstalled"}, ctx.obj.get('output_format', 'table'))
|
||||
|
||||
|
||||
@plugin.command()
|
||||
@click.argument("name")
|
||||
@click.option("--type", default="cli", help="Plugin type (cli, web, blockchain, ai)")
|
||||
@click.option("--description", default="", help="Plugin description")
|
||||
@click.option("--author", default="", help="Plugin author")
|
||||
@click.pass_context
|
||||
def create(ctx, name: str, type: str, description: str, author: str):
|
||||
"""Create a new plugin skeleton"""
|
||||
from .utils import output, success
|
||||
|
||||
plugin_dir = get_plugin_dir()
|
||||
plugin_file = plugin_dir / f"{name}.py"
|
||||
|
||||
if plugin_file.exists():
|
||||
from .utils import error
|
||||
error(f"Plugin '{name}' already exists")
|
||||
return
|
||||
|
||||
# Create plugin skeleton
|
||||
template = f'''"""{name} - {description}"""
|
||||
|
||||
import click
|
||||
|
||||
@click.group()
|
||||
def plugin_command():
|
||||
"""{name} plugin commands"""
|
||||
pass
|
||||
|
||||
@plugin_command.command()
|
||||
def hello():
|
||||
"""Hello from {name} plugin"""
|
||||
click.echo("Hello from {name} plugin!")
|
||||
'''
|
||||
|
||||
with open(plugin_file, "w") as f:
|
||||
f.write(template)
|
||||
|
||||
# Update manifest
|
||||
manifest_file = plugin_dir / "plugins.json"
|
||||
manifest = {"plugins": []}
|
||||
if manifest_file.exists():
|
||||
with open(manifest_file) as f:
|
||||
manifest = json.load(f)
|
||||
|
||||
manifest["plugins"].append({
|
||||
"name": name,
|
||||
"file": f"{name}.py",
|
||||
"description": description,
|
||||
"author": author,
|
||||
"type": type,
|
||||
"enabled": True
|
||||
})
|
||||
|
||||
with open(manifest_file, "w") as f:
|
||||
json.dump(manifest, f, indent=2)
|
||||
|
||||
success(f"Plugin '{name}' created")
|
||||
output({"name": name, "file": str(plugin_file), "type": type}, ctx.obj.get('output_format', 'table'))
|
||||
|
||||
|
||||
@plugin.command()
|
||||
@click.argument("name")
|
||||
@click.option("--output", default=".", help="Output directory")
|
||||
@click.pass_context
|
||||
def package(ctx, name: str, output: str):
|
||||
"""Package a plugin for distribution"""
|
||||
from .utils import output, success, error
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
import tarfile
|
||||
|
||||
plugin_dir = get_plugin_dir()
|
||||
manifest_file = plugin_dir / "plugins.json"
|
||||
|
||||
if not manifest_file.exists():
|
||||
error(f"Plugin '{name}' not found")
|
||||
return
|
||||
|
||||
with open(manifest_file) as f:
|
||||
manifest = json.load(f)
|
||||
|
||||
plugin_entry = next((p for p in manifest["plugins"] if p["name"] == name), None)
|
||||
if not plugin_entry:
|
||||
error(f"Plugin '{name}' not found")
|
||||
return
|
||||
|
||||
plugin_file = plugin_dir / plugin_entry["file"]
|
||||
if not plugin_file.exists():
|
||||
error(f"Plugin file '{plugin_entry['file']}' not found")
|
||||
return
|
||||
|
||||
# Create package
|
||||
output_dir = Path(output)
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
package_file = output_dir / f"{name}.tar.gz"
|
||||
|
||||
with tarfile.open(package_file, "w:gz") as tar:
|
||||
tar.add(plugin_file, arcname=plugin_file.name)
|
||||
# Add metadata
|
||||
metadata = json.dumps({
|
||||
"name": name,
|
||||
"description": plugin_entry.get("description", ""),
|
||||
"author": plugin_entry.get("author", ""),
|
||||
"type": plugin_entry.get("type", "cli"),
|
||||
"version": "1.0.0"
|
||||
})
|
||||
metadata_file = output_dir / "metadata.json"
|
||||
with open(metadata_file, "w") as f:
|
||||
f.write(metadata)
|
||||
tar.add(metadata_file, arcname="metadata.json")
|
||||
metadata_file.unlink()
|
||||
|
||||
success(f"Plugin '{name}' packaged to {package_file}")
|
||||
output({"name": name, "package": str(package_file)}, ctx.obj.get('output_format', 'table'))
|
||||
|
||||
|
||||
@plugin.command()
|
||||
@click.argument("name")
|
||||
@click.argument("state", type=click.Choice(["enable", "disable"]))
|
||||
|
||||
@@ -381,3 +381,37 @@ def handle_market_orders(args, default_coordinator_url, output_format, render_ma
|
||||
except Exception as e:
|
||||
print(f"Error getting orders: {e}")
|
||||
return
|
||||
|
||||
|
||||
def handle_market_list_plugins(args, default_coordinator_url, output_format, render_mapping):
|
||||
"""Handle marketplace plugin listing command."""
|
||||
marketplace_url = _marketplace_url(args, default_coordinator_url)
|
||||
|
||||
print(f"Getting marketplace plugins from {marketplace_url}...")
|
||||
try:
|
||||
response = requests.get(f"{marketplace_url}/v1/marketplace/plugins", timeout=10)
|
||||
if response.status_code == 200:
|
||||
plugins = response.json()
|
||||
if output_format(args) == "json":
|
||||
print(json.dumps(plugins, indent=2))
|
||||
return
|
||||
if isinstance(plugins, dict):
|
||||
plugins = plugins.get("plugins", [])
|
||||
print("Available marketplace plugins:")
|
||||
if not plugins:
|
||||
print(" No plugins found")
|
||||
return
|
||||
for plugin in plugins:
|
||||
print(f" - ID: {plugin.get('id', 'N/A')}")
|
||||
print(f" Name: {plugin.get('name', 'N/A')}")
|
||||
print(f" Type: {plugin.get('type', 'N/A')}")
|
||||
print(f" Author: {plugin.get('author', 'N/A')}")
|
||||
print(f" Description: {plugin.get('description', 'N/A')}")
|
||||
print(f" Version: {plugin.get('version', 'N/A')}")
|
||||
else:
|
||||
print(f"Query failed: {response.status_code}")
|
||||
print(f"Error: {response.text}")
|
||||
return
|
||||
except Exception as e:
|
||||
print(f"Error getting plugins: {e}")
|
||||
return
|
||||
|
||||
@@ -104,3 +104,8 @@ def register(subparsers: argparse._SubParsersAction, ctx: ParserContext) -> None
|
||||
market_orders_parser.add_argument("--rpc-url", default=ctx.default_rpc_url)
|
||||
market_orders_parser.add_argument("--marketplace-url")
|
||||
market_orders_parser.set_defaults(handler=ctx.handle_market_orders)
|
||||
|
||||
market_plugins_parser = market_subparsers.add_parser("list-plugin", help="List marketplace plugins")
|
||||
market_plugins_parser.add_argument("--coordinator-url", default=ctx.default_coordinator_url)
|
||||
market_plugins_parser.add_argument("--marketplace-url")
|
||||
market_plugins_parser.set_defaults(handler=ctx.handle_market_list_plugins)
|
||||
|
||||
Reference in New Issue
Block a user