Implement secure configuration management with encryption and validation (Phase 1 improvements)

This commit is contained in:
truetype committed 2026-03-19 20:29:30 +01:00
commit d4d6aafea9
12 files changed
+1656

No files matched your search

+378
View File
@@ -0,0 +1,378 @@
"""
Command-line interface for managing MaxClaw Agent configurations
"""
import sys
import argparse
from typing import Optional
from config_manager import config_manager
def print_configurations():
"""Print all configurations in a formatted way"""
configs = config_manager.get_configurations()
active = config_manager.get_active_configuration()
if not configs:
print("No configurations found.")
return
print("\nMaxClaw Agent Configurations:")
print("=" * 50)
for name, config in configs.items():
marker = " * " if name == active else " "
provider = config.get("provider", "unknown")
model = config.get("model_name", "unknown")
print(f"{marker}{name}:")
print(f" Provider: {provider}")
print(f" Model: {model}")
# Show additional info based on provider
if provider == "openai":
print(f" API Key: {'Set' if config.get('api_key') else 'Not set'}")
elif provider == "ollama":
base_url = config.get("base_url", "http://localhost:11434")
print(f" Base URL: {base_url}")
elif provider == "openrouter":
print(f" API Key: {'Set' if config.get('api_key') else 'Not set'}")
base_url = config.get("base_url", "https://openrouter.ai/api/v1")
print(f" Base URL: {base_url}")
print()
def add_configuration_interactive():
"""Interactively add a new configuration"""
print("\nAdd New Configuration")
print("=" * 30)
name = input("Configuration name: ").strip()
if not name:
print("Error: Configuration name cannot be empty")
return
if config_manager.configuration_exists(name):
overwrite = input(f"Configuration '{name}' already exists. Overwrite? (y/N): ").strip().lower()
if overwrite != 'y':
print("Cancelled.")
return
print("\nSelect provider:")
print("1. OpenAI")
print("2. Ollama")
print("3. OpenRouter")
provider_choice = input("Enter choice (1-3): ").strip()
provider_map = {
"1": "openai",
"2": "ollama",
"3": "openrouter"
}
if provider_choice not in provider_map:
print("Invalid choice.")
return
provider = provider_map[provider_choice]
config = {"provider": provider}
# Get model name
model_name = input(f"Model name for {provider} (e.g., gpt-4o, gemma2:9b): ").strip()
if not model_name:
print("Error: Model name cannot be empty")
return
config["model_name"] = model_name
# Get provider-specific settings
if provider == "openai":
api_key = input("OpenAI API key: ").strip()
if not api_key:
print("Warning: API key is required for OpenAI")
config["api_key"] = api_key
elif provider == "ollama":
base_url = input(f"Ollama base URL (default: http://localhost:11434): ").strip()
if not base_url:
base_url = "http://localhost:11434"
config["base_url"] = base_url
elif provider == "openrouter":
api_key = input("OpenRouter API key: ").strip()
if not api_key:
print("Warning: API key is required for OpenRouter")
config["api_key"] = api_key
base_url = input(f"OpenRouter base URL (default: https://openrouter.ai/api/v1): ").strip()
if not base_url:
base_url = "https://openrouter.ai/api/v1"
config["base_url"] = base_url
# Ask if this should be the active configuration
set_active = input("Set as active configuration? (y/N): ").strip().lower()
# Save the configuration
try:
config_manager.add_configuration(name, config)
print(f"\nConfiguration '{name}' saved successfully.")
if set_active == 'y':
config_manager.set_active_configuration(name)
print(f"Configuration '{name}' set as active.")
except ValueError as e:
print(f"Error: {e}")
def remove_configuration_interactive():
"""Interactively remove a configuration"""
configs = config_manager.get_configurations()
if not configs:
print("No configurations to remove.")
return
print("\nRemove Configuration")
print("=" * 30)
print_configurations()
name = input("Enter configuration name to remove: ").strip()
if not name:
print("Error: Configuration name cannot be empty")
return
if not config_manager.configuration_exists(name):
print(f"Error: Configuration '{name}' not found.")
return
active = config_manager.get_active_configuration()
if name == active:
confirm = input(f"'{name}' is the active configuration. Remove it anyway? (y/N): ").strip().lower()
if confirm != 'y':
print("Cancelled.")
return
confirm = input(f"Are you sure you want to remove configuration '{name}'? (y/N): ").strip().lower()
if confirm == 'y':
if config_manager.remove_configuration(name):
print(f"Configuration '{name}' removed successfully.")
if name == active:
print("Active configuration has been unset.")
else:
print(f"Error: Failed to remove configuration '{name}'.")
else:
print("Cancelled.")
def set_active_configuration_interactive():
"""Interactively set the active configuration"""
configs = config_manager.get_configurations()
if not configs:
print("No configurations available.")
return
print("\nSet Active Configuration")
print("=" * 30)
print_configurations()
name = input("Enter configuration name to set as active: ").strip()
if not name:
print("Error: Configuration name cannot be empty")
return
if not config_manager.configuration_exists(name):
print(f"Error: Configuration '{name}' not found.")
return
if config_manager.set_active_configuration(name):
print(f"Configuration '{name}' set as active.")
else:
print(f"Error: Failed to set configuration '{name}' as active.")
def show_active_configuration():
"""Show the currently active configuration"""
active_name = config_manager.get_active_configuration()
if not active_name:
print("No active configuration set.")
return
config = config_manager.get_configuration(active_name)
if not config:
print(f"Error: Active configuration '{active_name}' not found.")
return
print("\nActive Configuration")
print("=" * 30)
print(f"Name: {active_name}")
print(f"Provider: {config.get('provider', 'unknown')}")
print(f"Model: {config.get('model_name', 'unknown')}")
# Show additional info based on provider
provider = config.get('provider')
if provider == "openai":
print(f"API Key: {'Set' if config.get('api_key') else 'Not set'}")
elif provider == "ollama":
base_url = config.get('base_url', 'http://localhost:11434')
print(f"Base URL: {base_url}")
elif provider == "openrouter":
print(f"API Key: {'Set' if config.get('api_key') else 'Not set'}")
base_url = config.get('base_url', 'https://openrouter.ai/api/v1')
print(f"Base URL: {base_url}")
def test_configuration():
"""Test connection to a configuration"""
configs = config_manager.get_configurations()
if not configs:
print("No configurations to test.")
return
print("\nTest Configuration Connection")
print("=" * 30)
print_configurations()
name = input("Enter configuration name to test: ").strip()
if not name:
print("Error: Configuration name cannot be empty")
return
if not config_manager.configuration_exists(name):
print(f"Error: Configuration '{name}' not found.")
return
print(f"\nTesting connection to '{name}'...")
success, message = config_manager.test_configuration_connection(name)
if success:
print(f"✅ {message}")
else:
print(f"❌ {message}")
def main():
"""Main CLI entry point"""
parser = argparse.ArgumentParser(description="MaxClaw Agent Configuration Manager")
subparsers = parser.add_subparsers(dest='command', help='Available commands')
# List command
subparsers.add_parser('list', help='List all configurations')
# Add command
add_parser = subparsers.add_parser('add', help='Add a new configuration')
add_parser.add_argument('--non-interactive', action='store_true',
help='Non-interactive mode (requires all parameters)')
add_parser.add_argument('--name', help='Configuration name')
add_parser.add_argument('--provider', choices=['openai', 'ollama', 'openrouter'], help='Provider')
add_parser.add_argument('--model', help='Model name')
add_parser.add_argument('--api-key', help='API key (for OpenAI/OpenRouter)')
add_parser.add_argument('--base-url', help='Base URL (for Ollama/OpenRouter)')
add_parser.add_argument('--set-active', action='store_true', help='Set as active configuration')
# Remove command
remove_parser = subparsers.add_parser('remove', help='Remove a configuration')
remove_parser.add_argument('name', nargs='?', help='Configuration name to remove')
# Set active command
active_parser = subparsers.add_parser('set-active', help='Set active configuration')
active_parser.add_argument('name', nargs='?', help='Configuration name to set as active')
# Show active command
subparsers.add_parser('active', help='Show active configuration')
# Test command
test_parser = subparsers.add_parser('test', help='Test connection to a configuration')
test_parser.add_argument('name', nargs='?', help='Configuration name to test')
args = parser.parse_args()
if args.command == 'list':
print_configurations()
elif args.command == 'add':
if args.non_interactive:
# Non-interactive mode
if not all([args.name, args.provider, args.model]):
print("Error: --name, --provider, and --model are required for non-interactive mode")
return 1
config = {
"provider": args.provider,
"model_name": args.model
}
if args.provider in ['openai', 'openrouter']:
if not args.api_key:
print("Error: --api-key is required for OpenAI/OpenRouter providers")
return 1
config["api_key"] = args.api_key
if args.provider in ['ollama', 'openrouter']:
config["base_url"] = args.base_url or (
"http://localhost:11434" if args.provider == "ollama"
else "https://openrouter.ai/api/v1"
)
try:
config_manager.add_configuration(args.name, config)
print(f"Configuration '{args.name}' added successfully.")
if args.set_active:
config_manager.set_active_configuration(args.name)
print(f"Configuration '{args.name}' set as active.")
except ValueError as e:
print(f"Error: {e}")
return 1
else:
# Interactive mode
add_configuration_interactive()
elif args.command == 'remove':
if args.name:
# Non-interactive removal
if config_manager.remove_configuration(args.name):
print(f"Configuration '{args.name}' removed successfully.")
active = config_manager.get_active_configuration()
if args.name == active:
print("Active configuration has been unset.")
else:
print(f"Error: Configuration '{args.name}' not found.")
return 1
else:
# Interactive removal
remove_configuration_interactive()
elif args.command == 'set-active':
if args.name:
# Non-interactive setting
if config_manager.set_active_configuration(args.name):
print(f"Configuration '{args.name}' set as active.")
else:
print(f"Error: Configuration '{args.name}' not found.")
return 1
else:
# Interactive setting
set_active_configuration_interactive()
elif args.command == 'active':
show_active_configuration()
elif args.command == 'test':
if args.name:
# Non-interactive test
print(f"Testing connection to '{args.name}'...")
success, message = config_manager.test_configuration_connection(args.name)
if success:
print(f"✅ {message}")
else:
print(f"❌ {message}")
return 1
else:
# Interactive test
test_configuration()
else:
parser.print_help()
return 0
if __name__ == "__main__":
sys.exit(main())