Phase 2: Enhanced TUI/CLI with search, export/import, and custom tools for Proxmox and Ollama

This commit is contained in:
truetype committed 2026-03-20 16:08:26 +01:00
1 parent f5026965f7
commit 9f3298fa13
7 files changed
+1353 -16

No files matched your search

+253 -4
View File
@@ -1,22 +1,27 @@
"""
Command-line interface for managing MaxClaw Agent configurations
Enhanced with export/import, search, and better formatting
"""
import sys
import argparse
from typing import Optional
import json
import yaml
from pathlib import Path
from typing import Optional, Dict, Any
from config_manager import config_manager
def print_configurations():
def print_configurations(configs=None, title="MaxClaw Agent Configurations"):
"""Print all configurations in a formatted way"""
configs = config_manager.get_configurations()
if configs is None:
configs = config_manager.get_configurations()
active = config_manager.get_active_configuration()
if not configs:
print("No configurations found.")
return
print("\nMaxClaw Agent Configurations:")
print(f"\n{title}:")
print("=" * 50)
for name, config in configs.items():
@@ -41,6 +46,41 @@ def print_configurations():
print()
def filter_configurations(configs: Dict[str, Any], search_term: str) -> Dict[str, Any]:
"""Filter configurations based on search term"""
if not search_term:
return configs
search_term = search_term.lower()
filtered = {}
for name, config in configs.items():
# Search in name
if search_term in name.lower():
filtered[name] = config
continue
# Search in provider
provider = config.get("provider", "").lower()
if search_term in provider:
filtered[name] = config
continue
# Search in model name
model_name = config.get("model_name", "").lower()
if search_term in model_name:
filtered[name] = config
continue
# Search in base URL
base_url = config.get("base_url", "").lower()
if search_term in base_url:
filtered[name] = config
continue
return filtered
def add_configuration_interactive():
"""Interactively add a new configuration"""
print("\nAdd New Configuration")
@@ -247,6 +287,122 @@ def test_configuration():
print(f"❌ {message}")
def search_configurations():
"""Search/filter configurations"""
configs = config_manager.get_configurations()
if not configs:
print("No configurations to search.")
return
search_term = input("Search term (name, provider, model, URL): ").strip()
if not search_term:
print("Search cancelled.")
return
filtered = filter_configurations(configs, search_term)
if not filtered:
print(f"No configurations found matching '{search_term}'.")
else:
print_configurations(filtered, f"Search Results for '{search_term}'")
def export_configurations():
"""Export configurations to JSON or YAML"""
configs = config_manager.get_configurations()
if not configs:
print("No configurations to export.")
return
print("\nExport Configurations")
print("=" * 30)
# Select format
print("Select export format:")
print("1. JSON")
print("2. YAML")
format_choice = input("Enter choice (1-2): ").strip()
if format_choice not in ["1", "2"]:
print("Invalid choice.")
return
# Get filename
filename = input("Filename (without extension): ").strip()
if not filename:
print("Cancelled.")
return
# Export configurations
try:
if format_choice == "1": # JSON
export_file = Path(f"{filename}.json")
with open(export_file, 'w') as f:
json.dump(configs, f, indent=2)
else: # YAML
export_file = Path(f"{filename}.yaml")
with open(export_file, 'w') as f:
yaml.dump(configs, f, default_flow_style=False)
print(f"✅ Configurations exported to {export_file}")
except Exception as e:
print(f"❌ Error exporting configurations: {e}")
def import_configurations():
"""Import configurations from JSON or YAML"""
print("\nImport Configurations")
print("=" * 30)
# Get filename
filename = input("Filename (with extension .json or .yaml): ").strip()
if not filename:
print("Cancelled.")
return
# Check if file exists
file_path = Path(filename)
if not file_path.exists():
print(f"❌ Error: File '{filename}' not found.")
return
# Import configurations
try:
with open(file_path, 'r') as f:
if filename.endswith('.json'):
imported_configs = json.load(f)
elif filename.endswith(('.yaml', '.yml')):
imported_configs = yaml.safe_load(f)
else:
print("❌ Error: Unsupported file format. Use .json or .yaml")
return
# Validate imported configurations
if not isinstance(imported_configs, dict):
print("❌ Error: Invalid configuration format.")
return
# Ask for confirmation
confirm = input(f"Import {len(imported_configs)} configurations? (y/N): ").strip().lower()
if confirm != 'y':
print("Cancelled.")
return
# Import configurations
imported_count = 0
for name, config in imported_configs.items():
try:
config_manager.add_configuration(name, config)
imported_count += 1
print(f"✅ Imported configuration: {name}")
except Exception as e:
print(f"⚠️ Warning: Could not import '{name}': {e}")
print(f"\n✅ Successfully imported {imported_count} configurations.")
except Exception as e:
print(f"❌ Error importing configurations: {e}")
def main():
"""Main CLI entry point"""
parser = argparse.ArgumentParser(description="MaxClaw Agent Configuration Manager")
@@ -255,6 +411,10 @@ def main():
# List command
subparsers.add_parser('list', help='List all configurations')
# Search command
search_parser = subparsers.add_parser('search', help='Search configurations')
search_parser.add_argument('term', nargs='?', help='Search term')
# Add command
add_parser = subparsers.add_parser('add', help='Add a new configuration')
add_parser.add_argument('--non-interactive', action='store_true',
@@ -281,11 +441,33 @@ def main():
test_parser = subparsers.add_parser('test', help='Test connection to a configuration')
test_parser.add_argument('name', nargs='?', help='Configuration name to test')
# Export command
export_parser = subparsers.add_parser('export', help='Export configurations to JSON/YAML')
export_parser.add_argument('--format', choices=['json', 'yaml'], help='Export format')
export_parser.add_argument('--output', help='Output filename (without extension)')
# Import command
import_parser = subparsers.add_parser('import', help='Import configurations from JSON/YAML')
import_parser.add_argument('file', nargs='?', help='File to import')
args = parser.parse_args()
if args.command == 'list':
print_configurations()
elif args.command == 'search':
if args.term:
# Non-interactive search
configs = config_manager.get_configurations()
filtered = filter_configurations(configs, args.term)
if not filtered:
print(f"No configurations found matching '{args.term}'.")
else:
print_configurations(filtered, f"Search Results for '{args.term}'")
else:
# Interactive search
search_configurations()
elif args.command == 'add':
if args.non_interactive:
# Non-interactive mode
@@ -368,6 +550,73 @@ def main():
# Interactive test
test_configuration()
elif args.command == 'export':
if args.format and args.output:
# Non-interactive export
configs = config_manager.get_configurations()
if not configs:
print("No configurations to export.")
return 1
try:
if args.format == 'json':
export_file = Path(f"{args.output}.json")
with open(export_file, 'w') as f:
json.dump(configs, f, indent=2)
else: # yaml
export_file = Path(f"{args.output}.yaml")
with open(export_file, 'w') as f:
yaml.dump(configs, f, default_flow_style=False)
print(f"✅ Configurations exported to {export_file}")
except Exception as e:
print(f"❌ Error exporting configurations: {e}")
return 1
else:
# Interactive export
export_configurations()
elif args.command == 'import':
if args.file:
# Non-interactive import
file_path = Path(args.file)
if not file_path.exists():
print(f"❌ Error: File '{args.file}' not found.")
return 1
try:
with open(file_path, 'r') as f:
if args.file.endswith('.json'):
imported_configs = json.load(f)
elif args.file.endswith(('.yaml', '.yml')):
imported_configs = yaml.safe_load(f)
else:
print("❌ Error: Unsupported file format. Use .json or .yaml")
return 1
# Validate imported configurations
if not isinstance(imported_configs, dict):
print("❌ Error: Invalid configuration format.")
return 1
# Import configurations
imported_count = 0
for name, config in imported_configs.items():
try:
config_manager.add_configuration(name, config)
imported_count += 1
print(f"✅ Imported configuration: {name}")
except Exception as e:
print(f"⚠️ Warning: Could not import '{name}': {e}")
print(f"\n✅ Successfully imported {imported_count} configurations.")
except Exception as e:
print(f"❌ Error importing configurations: {e}")
return 1
else:
# Interactive import
import_configurations()
else:
parser.print_help()