From 99ef26e464a9eed5587ec514bcf186c99a1eb7c9 Mon Sep 17 00:00:00 2001 From: aitbc Date: Tue, 26 May 2026 13:14:03 +0200 Subject: [PATCH] fix: add TTY handling to transactions commands and improve genesis info - Added sys.stdin.isatty() check before getpass in transactions send and batch commands - Added environment variable fallback (AITBC_WALLET_PASSWORD) for non-interactive environments - Added --data-dir option to genesis info command to allow custom data directory paths - Improved error messages in genesis info to show chain_id and data directory - Fixes termios.error in non-interactive environments for transactions --- cli/aitbc_cli/commands/genesis.py | 11 ++++++-- cli/aitbc_cli/commands/transactions.py | 36 +++++++++++++++++++++++--- 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/cli/aitbc_cli/commands/genesis.py b/cli/aitbc_cli/commands/genesis.py index 7d626715..6b22d498 100644 --- a/cli/aitbc_cli/commands/genesis.py +++ b/cli/aitbc_cli/commands/genesis.py @@ -163,8 +163,9 @@ def verify(ctx, chain_id: str): @genesis.command() @click.option("--chain-id", default=None, help="Chain ID to show info for (auto-detected from config if not provided)") +@click.option("--data-dir", default=None, help="Data directory path (default: /var/lib/aitbc/data)") @click.pass_context -def info(ctx, chain_id: str): +def info(ctx, chain_id: str, data_dir: Optional[str]): """Show genesis block information""" # Auto-detect chain_id from config if not provided if not chain_id: @@ -172,12 +173,18 @@ def info(ctx, chain_id: str): config = get_config() chain_id = getattr(config, 'chain_id', 'ait-mainnet') + # Use provided data dir or default + if not data_dir: + data_dir = "/var/lib/aitbc/data" + import json import sqlite3 - genesis_path = Path(f"/var/lib/aitbc/data/{chain_id}/genesis.json") + genesis_path = Path(data_dir) / chain_id / "genesis.json" if not genesis_path.exists(): error(f"Genesis config not found: {genesis_path}") + error(f"Chain ID: {chain_id}, Data directory: {data_dir}") + error("Run 'aitbc genesis init' to create genesis block") return try: diff --git a/cli/aitbc_cli/commands/transactions.py b/cli/aitbc_cli/commands/transactions.py index a02e49ed..9d81be74 100644 --- a/cli/aitbc_cli/commands/transactions.py +++ b/cli/aitbc_cli/commands/transactions.py @@ -3,6 +3,8 @@ Transaction commands for AITBC CLI """ import json +import os +import sys from pathlib import Path from typing import Optional, Dict, Any, List @@ -136,8 +138,21 @@ def send(from_wallet: str, to_address: str, amount: float, fee: float, password: with open(password_file) as f: password = f.read().strip() elif not password: - import getpass - password = getpass.getpass("Enter wallet password: ") + # Check if we're in a TTY environment + if not sys.stdin.isatty(): + # Non-interactive: try environment variable + password = os.environ.get("AITBC_WALLET_PASSWORD") + if not password: + error("No TTY available for password prompt. Use --password or --password-file, or set AITBC_WALLET_PASSWORD environment variable.") + raise click.Abort() + else: + # Interactive: prompt for password + import getpass + try: + password = getpass.getpass("Enter wallet password: ") + except Exception as e: + error(f"Password prompt failed: {e}") + raise click.Abort() if not rpc_url: rpc_url = DEFAULT_RPC_URL @@ -158,8 +173,21 @@ def batch(transactions_file: str, password: Optional[str], password_file: Option with open(password_file) as f: password = f.read().strip() elif not password: - import getpass - password = getpass.getpass("Enter wallet password: ") + # Check if we're in a TTY environment + if not sys.stdin.isatty(): + # Non-interactive: try environment variable + password = os.environ.get("AITBC_WALLET_PASSWORD") + if not password: + error("No TTY available for password prompt. Use --password or --password-file, or set AITBC_WALLET_PASSWORD environment variable.") + raise click.Abort() + else: + # Interactive: prompt for password + import getpass + try: + password = getpass.getpass("Enter wallet password: ") + except Exception as e: + error(f"Password prompt failed: {e}") + raise click.Abort() if not rpc_url: rpc_url = DEFAULT_RPC_URL