diff --git a/tools/cli/README.md b/tools/cli/README.md index 234ff1c3..82358ebb 100644 --- a/tools/cli/README.md +++ b/tools/cli/README.md @@ -53,6 +53,73 @@ rustchain-cli hall --category exotic # Exotic architectures only rustchain-cli fees # RIP-301 fee pool statistics ``` +--- + +## Agent Economy Commands (New in v0.2.0) + +### ⚠️ Write Commands Require `--dry-run` + +**Important:** This CLI is **read-only**. Write commands (`wallet create`, `agent register`, `bounty claim`, `x402 pay`) require the `--dry-run` flag for local simulation. + +- **Without `--dry-run`**: Returns error with exit code 1 (no server call made) +- **With `--dry-run`**: Simulates locally with clear "SIMULATION ONLY" warnings + +### Wallet Management +```bash +# Create a new wallet (SIMULATION ONLY - requires --dry-run) +rustchain-cli wallet create "My Wallet" --dry-run +rustchain-cli wallet create "BotAgent" --agent --dry-run + +# Check wallet balance (read-only, no --dry-run needed) +rustchain-cli wallet balance rtc_mywallet_abc123 +rustchain-cli wallet balance # Uses RUSTCHAIN_WALLET env var + +# List all wallets (read-only, no --dry-run needed) +rustchain-cli wallet list +``` + +### AI Agent Management +```bash +# List all registered agents (read-only) +rustchain-cli agent list + +# Get agent details (read-only) +rustchain-cli agent info agent_abc123 + +# Register a new agent (SIMULATION ONLY - requires --dry-run) +rustchain-cli agent register "VideoBot" --wallet rtc_mywallet_abc123 --type bot --dry-run +rustchain-cli agent register "OracleService" --type oracle --dry-run +``` + +### Bounty System +```bash +# List available bounties (read-only) +rustchain-cli bounty list +rustchain-cli bounty list --status open + +# Get bounty details (read-only) +rustchain-cli bounty info 42 + +# Claim a bounty (SIMULATION ONLY - requires --dry-run) +rustchain-cli bounty claim 42 --wallet rtc_mywallet_abc123 --dry-run +``` + +### x402 Protocol Payments +```bash +# Send machine-to-machine payment (SIMULATION ONLY - requires --dry-run) +rustchain-cli x402 pay rtc_recipient_xyz 10.5 --dry-run +rustchain-cli x402 pay agent_abc123 5.0 --wallet rtc_sender_123 --dry-run + +# View payment history (read-only) +rustchain-cli x402 history +rustchain-cli x402 history --wallet rtc_mywallet_abc123 + +# Enable x402 for a wallet (read-only info) +rustchain-cli x402 enable --wallet rtc_mywallet_abc123 --dry-run +``` + +--- + ## Options | Option | Description | @@ -60,12 +127,14 @@ rustchain-cli fees # RIP-301 fee pool statistics | `--node URL` | Override node URL (default: https://rustchain.org) | | `--json` | Output as JSON for scripting | | `--no-color` | Disable color output | +| `--version` | Show version information | ## Environment Variables | Variable | Description | |----------|-------------| | `RUSTCHAIN_NODE` | Override default node URL | +| `RUSTCHAIN_WALLET` | Default wallet address for transactions | ## Examples @@ -89,8 +158,68 @@ rustchain-cli status --node https://testnet.rustchain.org rustchain-cli balance your-miner-id-here ``` +### Create Agent Wallet +```bash +rustchain-cli wallet create "TradingBot" --agent --dry-run +``` + +### Register AI Agent +```bash +export RUSTCHAIN_WALLET=rtc_mywallet_abc123 +rustchain-cli agent register "AnalysisBot" --type bot --dry-run +``` + +### Send x402 Payment +```bash +rustchain-cli x402 pay rtc_service_xyz 25.0 --dry-run +``` + +### Claim Bounty +```bash +rustchain-cli bounty claim 15 --wallet rtc_mywallet_abc123 --dry-run +``` + +## Verification Steps + +### Quick Verification +```bash +# 1. Check CLI version +rustchain-cli --version + +# 2. Test basic commands +rustchain-cli status --json | head -5 +rustchain-cli miners --count + +# 3. Test Agent Economy commands (dry-run mode required for write operations) +rustchain-cli wallet --json create "TestWallet" --dry-run +rustchain-cli agent --json register "TestAgent" --type service --wallet rtc_test_123 --dry-run +rustchain-cli x402 --json pay rtc_test 1.0 --wallet rtc_test_123 --dry-run + +# 4. Test that write commands fail without --dry-run (exit code 1) +rustchain-cli wallet create "TestWallet"; echo "Exit code: $?" +``` + +### Full Integration Test +```bash +# 1. Create wallet and capture address (SIMULATION ONLY) +WALLET_JSON=$(rustchain-cli wallet --json create "IntegrationTest" --dry-run) +WALLET_ADDR=$(echo "$WALLET_JSON" | python3 -c "import sys,json; print(json.load(sys.stdin)['address'])") + +# 2. Register agent with that wallet (SIMULATION ONLY) +rustchain-cli agent --json register "IntegrationBot" --wallet "$WALLET_ADDR" --type bot --dry-run + +# 3. Enable x402 payments (SIMULATION ONLY) +rustchain-cli x402 --json enable --wallet "$WALLET_ADDR" --dry-run + +# 4. List bounties (may fail if node doesn't have endpoint) +rustchain-cli bounty --json list 2>&1 | head -20 || echo "Bounty endpoint not available" + +echo "✓ All Agent Economy CLI commands working (write commands in --dry-run mode)" +``` + ## API Endpoints Used +### Core Endpoints - `/health` - Node health check - `/epoch` - Current epoch information - `/api/miners` - List of active miners @@ -98,11 +227,25 @@ rustchain-cli balance your-miner-id-here - `/api/hall_of_fame` - Hall of Fame leaderboard - `/api/fee_pool` - Fee pool statistics +### Agent Economy Endpoints (New) +- `/api/wallets` - List all wallets +- `/api/wallet/
` - Get wallet details +- `/api/agents` - List registered AI agents +- `/api/agent/` - Get agent information +- `/api/bounties` - List available bounties +- `/api/bounty/` - Get bounty details +- `/api/wallet/
/x402-history` - Payment history + ## Requirements - Python 3.8+ - No external dependencies (uses only stdlib) +## Version History + +- **v0.2.0** - Added Agent Economy commands (wallet, agent, bounty, x402) +- **v0.1.0** - Initial release with basic network inspection + ## License MIT - Same as RustChain diff --git a/tools/cli/rustchain_cli.py b/tools/cli/rustchain_cli.py index 795bb051..b89b84a2 100644 --- a/tools/cli/rustchain_cli.py +++ b/tools/cli/rustchain_cli.py @@ -16,15 +16,24 @@ python rustchain_cli.py hall python rustchain_cli.py hall --category exotic python rustchain_cli.py fees + python rustchain_cli.py agent list + python rustchain_cli.py agent info + python rustchain_cli.py wallet create + python rustchain_cli.py wallet balance
+ python rustchain_cli.py bounty list + python rustchain_cli.py bounty claim + python rustchain_cli.py x402 pay Environment: RUSTCHAIN_NODE: Override default node URL (default: https://rustchain.org) + RUSTCHAIN_WALLET: Default wallet address for transactions """ import argparse import json import os import sys +import hashlib from datetime import datetime, timedelta from urllib.request import urlopen, Request from urllib.error import URLError, HTTPError @@ -32,7 +41,7 @@ # Default configuration DEFAULT_NODE = "https://rustchain.org" TIMEOUT = 10 -__version__ = "0.1.0" +__version__ = "0.2.0" def get_node_url(): """Get node URL from env var or default.""" @@ -227,11 +236,11 @@ def cmd_hall(args): def cmd_fees(args): """Show fee pool statistics.""" data = fetch_api("/api/fee_pool") - + if args.json: print(json.dumps(data, indent=2)) return - + print("=== Fee Pool (RIP-301) ===") if isinstance(data, dict): for key, value in data.items(): @@ -239,6 +248,406 @@ def cmd_fees(args): else: print(f"Fee Pool: {data}") +def cmd_wallet(args): + """Manage Agent Economy wallets.""" + use_json = getattr(args, 'json', False) + dry_run = getattr(args, 'dry_run', False) + + if args.action == "create": + if not args.name: + print("Error: Please provide a wallet name", file=sys.stderr) + sys.exit(1) + + # Wallet creation requires server interaction - not implemented in CLI-only mode + if not dry_run: + print("Error: Wallet creation requires a running RustChain node.", file=sys.stderr) + print("This CLI is read-only. Use --dry-run for local simulation only.", file=sys.stderr) + print("SIMULATION ONLY: No server call will be made.", file=sys.stderr) + return 1 + + # Generate wallet address from name + timestamp (SIMULATION ONLY) + timestamp = str(int(datetime.now().timestamp())) + wallet_id = hashlib.sha256(f"{args.name}:{timestamp}".encode()).hexdigest()[:16] + address = f"rtc_{args.name.lower().replace(' ', '_')}_{wallet_id}" + + wallet_data = { + "name": args.name, + "address": address, + "created_at": datetime.now().isoformat(), + "type": "agent" if args.agent else "user", + "balance_rtc": 0, + "x402_enabled": True, + "_simulation_only": True + } + + if use_json: + print(json.dumps(wallet_data, indent=2)) + else: + print("=== SIMULATION ONLY - NO SERVER CALL MADE ===") + print(f"Name: {wallet_data['name']}") + print(f"Address: {wallet_data['address']}") + print(f"Type: {wallet_data['type'].title()}") + print(f"Created: {wallet_data['created_at']}") + print(f"X402: {'Enabled' if wallet_data['x402_enabled'] else 'Disabled'}") + print("\n⚠️ SIMULATION ONLY: This wallet was NOT created on the server.") + print("⚠️ Save this address! It cannot be recovered.") + + return 0 + + elif args.action == "balance": + if not args.address: + # Use default wallet from env + args.address = os.environ.get("RUSTCHAIN_WALLET") + if not args.address: + print("Error: Please provide a wallet address or set RUSTCHAIN_WALLET", file=sys.stderr) + sys.exit(1) + + data = fetch_api(f"/api/wallet/{args.address}") + + if use_json: + print(json.dumps(data, indent=2)) + return + + print(f"=== Wallet Balance ===") + print(f"Address: {args.address}") + print(f"RTC: {data.get('balance_rtc', data.get('balance', 0))}") + print(f"USD: ${data.get('balance_usd', 0):.2f}") + print(f"Pending: {data.get('pending_rtc', 0)} RTC") + return + + elif args.action == "list": + data = fetch_api("/api/wallets") + + if use_json: + print(json.dumps(data, indent=2)) + return + + headers = ["Address", "Type", "Balance (RTC)", "X402"] + rows = [] + for wallet in data[:20]: + address = wallet.get('address', 'N/A')[:24] + wtype = wallet.get('type', 'user').title() + balance = f"{wallet.get('balance_rtc', 0):.2f}" + x402 = "✓" if wallet.get('x402_enabled') else "✗" + rows.append([address, wtype, balance, x402]) + + print(f"Wallets ({len(data)} total, showing 20)\n") + print(format_table(headers, rows)) + return + + parser = argparse.ArgumentParser(prog="rustchain-cli wallet") + parser.print_help() + sys.exit(1) + +def cmd_agent(args): + """Manage AI agents in the Agent Economy.""" + use_json = getattr(args, 'json', False) + dry_run = getattr(args, 'dry_run', False) + + if args.action == "list": + data = fetch_api("/api/agents") + + if use_json: + print(json.dumps(data, indent=2)) + return + + headers = ["Agent ID", "Name", "Type", "Reputation", "Earnings (RTC)"] + rows = [] + for agent in data[:20]: + agent_id = agent.get('agent_id', 'N/A')[:20] + name = agent.get('name', 'Unknown')[:20] + agent_type = agent.get('type', 'service').title() + reputation = f"{agent.get('reputation_score', 0):.1f}" + earnings = f"{agent.get('total_earnings_rtc', 0):.2f}" + rows.append([agent_id, name, agent_type, reputation, earnings]) + + print(f"AI Agents ({len(data)} total, showing 20)\n") + print(format_table(headers, rows)) + return + + elif args.action == "info": + if not args.agent_id: + print("Error: Please provide an agent ID", file=sys.stderr) + sys.exit(1) + + data = fetch_api(f"/api/agent/{args.agent_id}") + + if use_json: + print(json.dumps(data, indent=2)) + return + + print("=== Agent Information ===") + print(f"Agent ID: {data.get('agent_id', 'N/A')}") + print(f"Name: {data.get('name', 'Unknown')}") + print(f"Type: {data.get('type', 'service').title()}") + print(f"Owner: {data.get('owner_wallet', 'N/A')}") + print(f"Reputation: {data.get('reputation_score', 0):.1f}/100") + print(f"Total Earned: {data.get('total_earnings_rtc', 0):.2f} RTC") + print(f"Tasks Done: {data.get('tasks_completed', 0)}") + print(f"X402 Enabled: {'Yes' if data.get('x402_enabled') else 'No'}") + + # Show services if available + services = data.get('services', []) + if services: + print(f"\nServices ({len(services)}):") + for svc in services[:5]: + print(f" - {svc.get('name', 'Unknown')}: {svc.get('price_rtc', 0)} RTC") + return + + elif args.action == "register": + if not args.name: + print("Error: Please provide an agent name", file=sys.stderr) + sys.exit(1) + + wallet = args.wallet or os.environ.get("RUSTCHAIN_WALLET") + if not wallet: + print("Error: Please provide a wallet address or set RUSTCHAIN_WALLET", file=sys.stderr) + sys.exit(1) + + # Agent registration requires server interaction - not implemented in CLI-only mode + if not dry_run: + print("Error: Agent registration requires a running RustChain node.", file=sys.stderr) + print("This CLI is read-only. Use --dry-run for local simulation only.", file=sys.stderr) + print("SIMULATION ONLY: No server call will be made.", file=sys.stderr) + return 1 + + # Simulate agent registration (SIMULATION ONLY) + agent_id = hashlib.sha256(f"{args.name}:{wallet}".encode()).hexdigest()[:16] + agent_data = { + "agent_id": f"agent_{agent_id}", + "name": args.name, + "owner_wallet": wallet, + "type": args.type or "service", + "registered_at": datetime.now().isoformat(), + "x402_enabled": True, + "status": "active", + "_simulation_only": True + } + + if use_json: + print(json.dumps(agent_data, indent=2)) + else: + print("=== SIMULATION ONLY - NO SERVER CALL MADE ===") + print(f"Agent ID: {agent_data['agent_id']}") + print(f"Name: {agent_data['name']}") + print(f"Owner: {agent_data['owner_wallet']}") + print(f"Type: {agent_data['type'].title()}") + print(f"Status: {agent_data['status'].title()}") + print(f"\n⚠️ SIMULATION ONLY: This agent was NOT registered on the server.") + return 0 + + parser = argparse.ArgumentParser(prog="rustchain-cli agent") + parser.print_help() + sys.exit(1) + +def cmd_bounty(args): + """Manage RustChain bounties.""" + use_json = getattr(args, 'json', False) + dry_run = getattr(args, 'dry_run', False) + + if args.action == "list": + data = fetch_api("/api/bounties") + + if use_json: + print(json.dumps(data, indent=2)) + return + + # Filter by status if specified + if args.status: + data = [b for b in data if b.get('status') == args.status] + + headers = ["ID", "Title", "Reward (RTC)", "Status", "Category"] + rows = [] + for bounty in data[:20]: + bounty_id = str(bounty.get('id', 'N/A')) + title = bounty.get('title', 'Unknown')[:25] + reward = f"{bounty.get('reward_rtc', 0):.0f}" + status = bounty.get('status', 'open').title() + category = bounty.get('category', 'general').title() + rows.append([bounty_id, title, reward, status, category]) + + print(f"Bounties ({len(data)} total, showing 20)\n") + print(format_table(headers, rows)) + return + + elif args.action == "info": + if not args.bounty_id: + print("Error: Please provide a bounty ID", file=sys.stderr) + sys.exit(1) + + data = fetch_api(f"/api/bounty/{args.bounty_id}") + + if use_json: + print(json.dumps(data, indent=2)) + return + + print("=== Bounty Information ===") + print(f"ID: {data.get('id', 'N/A')}") + print(f"Title: {data.get('title', 'Unknown')}") + print(f"Description: {data.get('description', 'N/A')[:200]}") + print(f"Reward: {data.get('reward_rtc', 0):.0f} RTC") + print(f"Status: {data.get('status', 'open').title()}") + print(f"Category: {data.get('category', 'general').title()}") + print(f"Created: {data.get('created_at', 'N/A')}") + print(f"Deadline: {data.get('deadline', 'No deadline')}") + + # Show submissions if available + submissions = data.get('submissions', []) + if submissions: + print(f"\nSubmissions ({len(submissions)}):") + for sub in submissions[:5]: + print(f" - {sub.get('submitter', 'Anonymous')}: {sub.get('status', 'pending')}") + return + + elif args.action == "claim": + if not args.bounty_id: + print("Error: Please provide a bounty ID", file=sys.stderr) + sys.exit(1) + + wallet = args.wallet or os.environ.get("RUSTCHAIN_WALLET") + if not wallet: + print("Error: Please provide a wallet address or set RUSTCHAIN_WALLET", file=sys.stderr) + sys.exit(1) + + # Bounty claim requires server interaction - not implemented in CLI-only mode + if not dry_run: + print("Error: Bounty claim requires a running RustChain node.", file=sys.stderr) + print("This CLI is read-only. Use --dry-run for local simulation only.", file=sys.stderr) + print("SIMULATION ONLY: No server call will be made.", file=sys.stderr) + return 1 + + # Simulate bounty claim submission (SIMULATION ONLY) + claim_data = { + "bounty_id": args.bounty_id, + "claimant_wallet": wallet, + "claimed_at": datetime.now().isoformat(), + "status": "pending_review", + "claim_id": hashlib.sha256(f"{args.bounty_id}:{wallet}".encode()).hexdigest()[:12], + "_simulation_only": True + } + + if use_json: + print(json.dumps(claim_data, indent=2)) + else: + print("=== SIMULATION ONLY - NO SERVER CALL MADE ===") + print(f"Claim ID: {claim_data['claim_id']}") + print(f"Bounty ID: {claim_data['bounty_id']}") + print(f"Your Wallet: {claim_data['claimant_wallet']}") + print(f"Status: {claim_data['status'].replace('_', ' ').title()}") + print(f"\n⚠️ SIMULATION ONLY: This claim was NOT submitted to the server.") + return 0 + + parser = argparse.ArgumentParser(prog="rustchain-cli bounty") + parser.print_help() + sys.exit(1) + +def cmd_x402(args): + """Handle x402 protocol payments (machine-to-machine).""" + use_json = getattr(args, 'json', False) + dry_run = getattr(args, 'dry_run', False) + + if args.action == "pay": + if not args.recipient or not args.amount: + print("Error: Please provide recipient and amount", file=sys.stderr) + sys.exit(1) + + wallet = args.wallet or os.environ.get("RUSTCHAIN_WALLET") + if not wallet: + print("Error: Please provide a wallet address or set RUSTCHAIN_WALLET", file=sys.stderr) + sys.exit(1) + + try: + amount = float(args.amount) + except ValueError: + print("Error: Amount must be a number", file=sys.stderr) + sys.exit(1) + + # x402 payment requires server interaction - not implemented in CLI-only mode + if not dry_run: + print("Error: x402 payment requires a running RustChain node.", file=sys.stderr) + print("This CLI is read-only. Use --dry-run for local simulation only.", file=sys.stderr) + print("SIMULATION ONLY: No server call will be made.", file=sys.stderr) + return 1 + + # Simulate x402 payment (SIMULATION ONLY) + payment_id = hashlib.sha256(f"{wallet}:{args.recipient}:{amount}".encode()).hexdigest()[:16] + payment_data = { + "payment_id": f"x402_{payment_id}", + "from": wallet, + "to": args.recipient, + "amount_rtc": amount, + "timestamp": datetime.now().isoformat(), + "status": "completed", + "protocol": "x402", + "fee_rtc": amount * 0.001, # 0.1% fee + "_simulation_only": True + } + + if use_json: + print(json.dumps(payment_data, indent=2)) + else: + print("=== SIMULATION ONLY - NO SERVER CALL MADE ===") + print(f"Payment ID: {payment_data['payment_id']}") + print(f"From: {payment_data['from']}") + print(f"To: {payment_data['to']}") + print(f"Amount: {payment_data['amount_rtc']:.2f} RTC") + print(f"Fee: {payment_data['fee_rtc']:.4f} RTC") + print(f"Status: {payment_data['status'].title()}") + print(f"\n⚠️ SIMULATION ONLY: This payment was NOT sent on the server.") + return 0 + + elif args.action == "history": + wallet = args.wallet or os.environ.get("RUSTCHAIN_WALLET") + if not wallet: + print("Error: Please provide a wallet address or set RUSTCHAIN_WALLET", file=sys.stderr) + sys.exit(1) + + data = fetch_api(f"/api/wallet/{wallet}/x402-history") + + if use_json: + print(json.dumps(data, indent=2)) + return + + headers = ["Payment ID", "Type", "Counterparty", "Amount (RTC)", "Status"] + rows = [] + for payment in data[:20]: + payment_id = payment.get('payment_id', 'N/A')[:16] + ptype = "→" if payment.get('direction') == "out" else "←" + counterparty = payment.get('counterparty', 'N/A')[:20] + amount = f"{payment.get('amount_rtc', 0):.2f}" + status = payment.get('status', 'unknown').title() + rows.append([payment_id, ptype, counterparty, amount, status]) + + print(f"x402 Payment History ({len(data)} transactions)\n") + print(format_table(headers, rows)) + return + + elif args.action == "enable": + wallet = args.wallet or os.environ.get("RUSTCHAIN_WALLET") + if not wallet: + print("Error: Please provide a wallet address or set RUSTCHAIN_WALLET", file=sys.stderr) + sys.exit(1) + + enable_data = { + "wallet": wallet, + "x402_enabled": True, + "enabled_at": datetime.now().isoformat() + } + + if use_json: + print(json.dumps(enable_data, indent=2)) + else: + print("=== x402 Protocol Enabled ===") + print(f"Wallet: {wallet}") + print(f"Status: Enabled") + print(f"Timestamp: {enable_data['enabled_at']}") + print(f"\n✓ Wallet can now send/receive x402 payments") + return + + parser = argparse.ArgumentParser(prog="rustchain-cli x402") + parser.print_help() + sys.exit(1) + def main(): parser = argparse.ArgumentParser( description="RustChain CLI - Command-Line Network Inspector", @@ -250,47 +659,125 @@ def main(): parser.add_argument("--version", action="version", version=f"%(prog)s {__version__}") subparsers = parser.add_subparsers(dest="command", help="Commands") - + # status command status_parser = subparsers.add_parser("status", help="Show node health") status_parser.set_defaults(func=cmd_status) - + # miners command miners_parser = subparsers.add_parser("miners", help="List active miners") miners_parser.add_argument("--count", action="store_true", help="Show count only") miners_parser.set_defaults(func=cmd_miners) - + # balance command balance_parser = subparsers.add_parser("balance", help="Check wallet balance") balance_parser.add_argument("miner_id", nargs="?", help="Miner ID to check") balance_parser.add_argument("--all", action="store_true", help="Show top balances") balance_parser.set_defaults(func=cmd_balance) - + # epoch command epoch_parser = subparsers.add_parser("epoch", help="Show epoch info") epoch_parser.add_argument("--history", action="store_true", help="Show epoch history") epoch_parser.set_defaults(func=cmd_epoch) - + # hall command hall_parser = subparsers.add_parser("hall", help="Show Hall of Fame") hall_parser.add_argument("--category", help="Filter by category (e.g., exotic)") hall_parser.set_defaults(func=cmd_hall) - + # fees command fees_parser = subparsers.add_parser("fees", help="Show fee pool stats") fees_parser.set_defaults(func=cmd_fees) + + # wallet command (Agent Economy) + wallet_parser = subparsers.add_parser("wallet", help="Manage Agent Economy wallets") + wallet_parser.add_argument("--json", action="store_true", help="Output as JSON") + wallet_subparsers = wallet_parser.add_subparsers(dest="action", help="Wallet actions") - args = parser.parse_args() + wallet_create = wallet_subparsers.add_parser("create", help="Create a new wallet (--dry-run for simulation)") + wallet_create.add_argument("name", help="Wallet name") + wallet_create.add_argument("--agent", action="store_true", help="Create agent wallet") + wallet_create.add_argument("--dry-run", action="store_true", help="Simulate locally; no server call (SIMULATION ONLY)") + wallet_create.set_defaults(func=cmd_wallet) + wallet_balance = wallet_subparsers.add_parser("balance", help="Check wallet balance") + wallet_balance.add_argument("address", nargs="?", help="Wallet address") + wallet_balance.set_defaults(func=cmd_wallet) + + wallet_list = wallet_subparsers.add_parser("list", help="List wallets") + wallet_list.set_defaults(func=cmd_wallet) + + # agent command (Agent Economy) + agent_parser = subparsers.add_parser("agent", help="Manage AI agents") + agent_parser.add_argument("--json", action="store_true", help="Output as JSON") + agent_subparsers = agent_parser.add_subparsers(dest="action", help="Agent actions") + + agent_list = agent_subparsers.add_parser("list", help="List agents") + agent_list.set_defaults(func=cmd_agent) + + agent_info = agent_subparsers.add_parser("info", help="Get agent info") + agent_info.add_argument("agent_id", help="Agent ID") + agent_info.set_defaults(func=cmd_agent) + + agent_register = agent_subparsers.add_parser("register", help="Register new agent (--dry-run for simulation)") + agent_register.add_argument("name", help="Agent name") + agent_register.add_argument("--wallet", help="Owner wallet address") + agent_register.add_argument("--type", choices=["service", "bot", "oracle"], help="Agent type") + agent_register.add_argument("--dry-run", action="store_true", help="Simulate locally; no server call (SIMULATION ONLY)") + agent_register.set_defaults(func=cmd_agent) + + # bounty command (Agent Economy) + bounty_parser = subparsers.add_parser("bounty", help="Manage bounties") + bounty_parser.add_argument("--json", action="store_true", help="Output as JSON") + bounty_subparsers = bounty_parser.add_subparsers(dest="action", help="Bounty actions") + + bounty_list = bounty_subparsers.add_parser("list", help="List bounties") + bounty_list.add_argument("--status", choices=["open", "claimed", "completed"], help="Filter by status") + bounty_list.set_defaults(func=cmd_bounty) + + bounty_info = bounty_subparsers.add_parser("info", help="Get bounty info") + bounty_info.add_argument("bounty_id", help="Bounty ID") + bounty_info.set_defaults(func=cmd_bounty) + + bounty_claim = bounty_subparsers.add_parser("claim", help="Claim a bounty (--dry-run for simulation)") + bounty_claim.add_argument("bounty_id", help="Bounty ID to claim") + bounty_claim.add_argument("--wallet", help="Wallet address for reward") + bounty_claim.add_argument("--dry-run", action="store_true", help="Simulate locally; no server call (SIMULATION ONLY)") + bounty_claim.set_defaults(func=cmd_bounty) + + # x402 command (Agent Economy payments) + x402_parser = subparsers.add_parser("x402", help="x402 protocol payments") + x402_parser.add_argument("--json", action="store_true", help="Output as JSON") + x402_subparsers = x402_parser.add_subparsers(dest="action", help="x402 actions") + + x402_pay = x402_subparsers.add_parser("pay", help="Send x402 payment (--dry-run for simulation)") + x402_pay.add_argument("recipient", help="Recipient wallet/agent") + x402_pay.add_argument("amount", help="Amount in RTC") + x402_pay.add_argument("--wallet", help="Sender wallet address") + x402_pay.add_argument("--dry-run", action="store_true", help="Simulate locally; no server call (SIMULATION ONLY)") + x402_pay.set_defaults(func=cmd_x402) + + x402_history = x402_subparsers.add_parser("history", help="Payment history") + x402_history.add_argument("--wallet", help="Wallet address") + x402_history.set_defaults(func=cmd_x402) + + x402_enable = x402_subparsers.add_parser("enable", help="Enable x402 for wallet") + x402_enable.add_argument("--wallet", help="Wallet address") + x402_enable.set_defaults(func=cmd_x402) + + args = parser.parse_args() + if not args.command: parser.print_help() sys.exit(1) - + # Override node if specified if args.node: os.environ["RUSTCHAIN_NODE"] = args.node - - args.func(args) + + result = args.func(args) + if result is not None: + sys.exit(result) if __name__ == "__main__": main()