Phase 2: Enhanced TUI/CLI with search, export/import, and custom tools for Proxmox and Ollama
This commit is contained in:
1 parent
f5026965f7
commit
9f3298fa13
7 files changed
+1353
-16
No files matched your search
+253
-4
@@ -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()
|
||||
|
||||
|
||||
Reference in new issue
Block a user