Enhance deployment system with retry functionality and improved UX
Major Improvements: - Added retry deployment buttons in machine list for failed deployments - Added retry button in SSH console modal footer for enhanced UX - Enhanced deployment process with comprehensive cleanup of existing services - Improved binary installation with password-based sudo authentication - Updated configuration generation to include all required sections (agent, ai, network, security) - Fixed deployment verification and error handling Security Enhancements: - Enhanced verifiedStopExistingServices with thorough cleanup process - Improved binary copying with proper sudo authentication - Added comprehensive configuration validation UX Improvements: - Users can retry deployments without re-running machine discovery - Retry buttons available from both machine list and console modal - Real-time deployment progress with detailed console output - Clear error states with actionable retry options Technical Changes: - Modified ServiceDeployment.tsx with retry button components - Enhanced api/setup_manager.go with improved deployment functions - Updated main.go with command line argument support (--config, --setup) - Added comprehensive zero-trust security validation system 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
322
internal/hap/terminal.go
Normal file
322
internal/hap/terminal.go
Normal file
@@ -0,0 +1,322 @@
|
||||
package hap
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"chorus.services/bzzz/internal/common/runtime"
|
||||
"chorus.services/bzzz/logging"
|
||||
)
|
||||
|
||||
// TerminalInterface provides a terminal-based interface for human agents
|
||||
type TerminalInterface struct {
|
||||
services *runtime.RuntimeServices
|
||||
logger logging.Logger
|
||||
running bool
|
||||
scanner *bufio.Scanner
|
||||
}
|
||||
|
||||
// NewTerminalInterface creates a new terminal interface
|
||||
func NewTerminalInterface(services *runtime.RuntimeServices, logger logging.Logger) *TerminalInterface {
|
||||
return &TerminalInterface{
|
||||
services: services,
|
||||
logger: logger,
|
||||
running: false,
|
||||
scanner: bufio.NewScanner(os.Stdin),
|
||||
}
|
||||
}
|
||||
|
||||
// Start begins the terminal interface
|
||||
func (ti *TerminalInterface) Start(ctx context.Context) error {
|
||||
if ti.running {
|
||||
return fmt.Errorf("terminal interface is already running")
|
||||
}
|
||||
|
||||
ti.logger.Info("👤 Starting Human Agent Portal terminal interface")
|
||||
|
||||
// Display welcome message
|
||||
ti.displayWelcome()
|
||||
|
||||
// Start command processing in background
|
||||
go ti.processCommands(ctx)
|
||||
|
||||
ti.running = true
|
||||
ti.logger.Info("✅ Terminal interface ready for human interaction")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop gracefully stops the terminal interface
|
||||
func (ti *TerminalInterface) Stop(ctx context.Context) error {
|
||||
if !ti.running {
|
||||
return nil
|
||||
}
|
||||
|
||||
ti.logger.Info("🛑 Stopping terminal interface")
|
||||
ti.running = false
|
||||
|
||||
fmt.Println("\n👋 Human Agent Portal shutting down. Goodbye!")
|
||||
return nil
|
||||
}
|
||||
|
||||
// displayWelcome shows the welcome message and commands
|
||||
func (ti *TerminalInterface) displayWelcome() {
|
||||
fmt.Println("\n" + strings.Repeat("=", 60))
|
||||
fmt.Println("🎯 BZZZ Human Agent Portal (HAP)")
|
||||
fmt.Println(" Welcome to collaborative AI task coordination")
|
||||
fmt.Println(strings.Repeat("=", 60))
|
||||
|
||||
if ti.services.Node != nil {
|
||||
fmt.Printf("📍 Node ID: %s\n", ti.services.Node.ID().ShortString())
|
||||
}
|
||||
|
||||
if ti.services.Config != nil {
|
||||
fmt.Printf("🤖 Agent ID: %s\n", ti.services.Config.Agent.ID)
|
||||
if ti.services.Config.Agent.Role != "" {
|
||||
fmt.Printf("🎭 Role: %s\n", ti.services.Config.Agent.Role)
|
||||
}
|
||||
}
|
||||
|
||||
if ti.services.Node != nil {
|
||||
fmt.Printf("🌐 Connected Peers: %d\n", ti.services.Node.ConnectedPeers())
|
||||
}
|
||||
|
||||
fmt.Println("\n📋 Available Commands:")
|
||||
fmt.Println(" status - Show system status")
|
||||
fmt.Println(" peers - List connected peers")
|
||||
fmt.Println(" send <msg> - Send message to coordination channel")
|
||||
fmt.Println(" role - Show role information")
|
||||
fmt.Println(" tasks - Show task information")
|
||||
fmt.Println(" health - Show health status")
|
||||
fmt.Println(" help - Show this help message")
|
||||
fmt.Println(" quit/exit - Exit the interface")
|
||||
fmt.Println(strings.Repeat("-", 60))
|
||||
fmt.Print("HAP> ")
|
||||
}
|
||||
|
||||
// processCommands handles user input and commands
|
||||
func (ti *TerminalInterface) processCommands(ctx context.Context) {
|
||||
for ti.running && ti.scanner.Scan() {
|
||||
input := strings.TrimSpace(ti.scanner.Text())
|
||||
if input == "" {
|
||||
fmt.Print("HAP> ")
|
||||
continue
|
||||
}
|
||||
|
||||
// Parse command and arguments
|
||||
parts := strings.Fields(input)
|
||||
command := strings.ToLower(parts[0])
|
||||
|
||||
switch command {
|
||||
case "quit", "exit":
|
||||
ti.running = false
|
||||
return
|
||||
|
||||
case "help":
|
||||
ti.showHelp()
|
||||
|
||||
case "status":
|
||||
ti.showStatus()
|
||||
|
||||
case "peers":
|
||||
ti.showPeers()
|
||||
|
||||
case "role":
|
||||
ti.showRole()
|
||||
|
||||
case "tasks":
|
||||
ti.showTasks()
|
||||
|
||||
case "health":
|
||||
ti.showHealth()
|
||||
|
||||
case "send":
|
||||
if len(parts) < 2 {
|
||||
fmt.Println("❌ Usage: send <message>")
|
||||
} else {
|
||||
message := strings.Join(parts[1:], " ")
|
||||
ti.sendMessage(message)
|
||||
}
|
||||
|
||||
default:
|
||||
fmt.Printf("❌ Unknown command: %s (type 'help' for available commands)\n", command)
|
||||
}
|
||||
|
||||
fmt.Print("HAP> ")
|
||||
}
|
||||
}
|
||||
|
||||
// showHelp displays the help message
|
||||
func (ti *TerminalInterface) showHelp() {
|
||||
fmt.Println("\n📋 HAP Commands:")
|
||||
fmt.Println(" status - Show current system status")
|
||||
fmt.Println(" peers - List all connected P2P peers")
|
||||
fmt.Println(" send <msg> - Send message to coordination channel")
|
||||
fmt.Println(" role - Display role and capability information")
|
||||
fmt.Println(" tasks - Show active tasks (if any)")
|
||||
fmt.Println(" health - Display system health status")
|
||||
fmt.Println(" help - Show this help message")
|
||||
fmt.Println(" quit/exit - Exit the Human Agent Portal")
|
||||
}
|
||||
|
||||
// showStatus displays the current system status
|
||||
func (ti *TerminalInterface) showStatus() {
|
||||
fmt.Println("\n📊 System Status:")
|
||||
fmt.Println(strings.Repeat("-", 40))
|
||||
|
||||
if ti.services.Node != nil {
|
||||
fmt.Printf("🌐 P2P Status: Connected (%d peers)\n", ti.services.Node.ConnectedPeers())
|
||||
fmt.Printf("📍 Node ID: %s\n", ti.services.Node.ID().ShortString())
|
||||
}
|
||||
|
||||
if ti.services.Config != nil {
|
||||
fmt.Printf("🤖 Agent ID: %s\n", ti.services.Config.Agent.ID)
|
||||
fmt.Printf("🎭 Role: %s\n", ti.services.Config.Agent.Role)
|
||||
fmt.Printf("🎯 Specialization: %s\n", ti.services.Config.Agent.Specialization)
|
||||
}
|
||||
|
||||
// Service status
|
||||
fmt.Printf("📡 PubSub: %s\n", ti.getServiceStatus("PubSub", ti.services.PubSub != nil))
|
||||
fmt.Printf("🕸️ DHT: %s\n", ti.getServiceStatus("DHT", ti.services.DHT != nil))
|
||||
fmt.Printf("🔗 UCXI: %s\n", ti.getServiceStatus("UCXI", ti.services.UCXIServer != nil))
|
||||
fmt.Printf("🗳️ Elections: %s\n", ti.getServiceStatus("Elections", ti.services.ElectionManager != nil))
|
||||
fmt.Printf("❤️ Health: %s\n", ti.getServiceStatus("Health", ti.services.HealthManager != nil))
|
||||
|
||||
fmt.Printf("⏰ Uptime: %s\n", time.Since(time.Now().Add(-5*time.Minute)).String()) // Placeholder
|
||||
}
|
||||
|
||||
// showPeers displays connected peers
|
||||
func (ti *TerminalInterface) showPeers() {
|
||||
fmt.Println("\n🌐 Connected Peers:")
|
||||
fmt.Println(strings.Repeat("-", 40))
|
||||
|
||||
if ti.services.Node != nil {
|
||||
peerCount := ti.services.Node.ConnectedPeers()
|
||||
fmt.Printf("Total Connected: %d\n", peerCount)
|
||||
|
||||
if peerCount == 0 {
|
||||
fmt.Println("No peers currently connected")
|
||||
fmt.Println("💡 Tip: Make sure other BZZZ nodes are running on your network")
|
||||
} else {
|
||||
fmt.Println("🔍 Use P2P tools to see detailed peer information")
|
||||
}
|
||||
} else {
|
||||
fmt.Println("❌ P2P node not available")
|
||||
}
|
||||
}
|
||||
|
||||
// showRole displays role and capability information
|
||||
func (ti *TerminalInterface) showRole() {
|
||||
fmt.Println("\n🎭 Role Information:")
|
||||
fmt.Println(strings.Repeat("-", 40))
|
||||
|
||||
if ti.services.Config != nil {
|
||||
cfg := ti.services.Config
|
||||
fmt.Printf("Role: %s\n", cfg.Agent.Role)
|
||||
fmt.Printf("Expertise: %v\n", cfg.Agent.Expertise)
|
||||
fmt.Printf("Reports To: %v\n", cfg.Agent.ReportsTo)
|
||||
fmt.Printf("Deliverables: %v\n", cfg.Agent.Deliverables)
|
||||
fmt.Printf("Capabilities: %v\n", cfg.Agent.Capabilities)
|
||||
fmt.Printf("Specialization: %s\n", cfg.Agent.Specialization)
|
||||
|
||||
// Authority level
|
||||
if authority, err := cfg.GetRoleAuthority(cfg.Agent.Role); err == nil {
|
||||
fmt.Printf("Authority Level: %s\n", authority)
|
||||
}
|
||||
} else {
|
||||
fmt.Println("❌ Configuration not available")
|
||||
}
|
||||
}
|
||||
|
||||
// showTasks displays task information
|
||||
func (ti *TerminalInterface) showTasks() {
|
||||
fmt.Println("\n📋 Task Information:")
|
||||
fmt.Println(strings.Repeat("-", 40))
|
||||
|
||||
// HAP doesn't execute tasks like agents, but can show coordination status
|
||||
fmt.Println("📝 HAP Role: Human interaction facilitator")
|
||||
fmt.Println("🎯 Purpose: Coordinate with autonomous agents")
|
||||
fmt.Println("💼 Current Mode: Interactive terminal")
|
||||
|
||||
if ti.services.TaskCoordinator != nil {
|
||||
fmt.Println("✅ Task coordination system is active")
|
||||
} else {
|
||||
fmt.Println("❌ Task coordination system not available")
|
||||
}
|
||||
}
|
||||
|
||||
// showHealth displays health status
|
||||
func (ti *TerminalInterface) showHealth() {
|
||||
fmt.Println("\n❤️ Health Status:")
|
||||
fmt.Println(strings.Repeat("-", 40))
|
||||
|
||||
if ti.services.HealthManager != nil {
|
||||
status := ti.services.HealthManager.GetOverallStatus()
|
||||
healthIcon := "✅"
|
||||
if !status.Healthy {
|
||||
healthIcon = "❌"
|
||||
}
|
||||
fmt.Printf("%s Overall Health: %s\n", healthIcon, ti.boolToStatus(status.Healthy))
|
||||
fmt.Printf("📋 Details: %s\n", status.Message)
|
||||
fmt.Printf("⏰ Last Check: %s\n", status.Timestamp.Format(time.RFC3339))
|
||||
} else {
|
||||
fmt.Println("❌ Health manager not available")
|
||||
}
|
||||
}
|
||||
|
||||
// sendMessage sends a message to the coordination channel
|
||||
func (ti *TerminalInterface) sendMessage(message string) {
|
||||
if ti.services.PubSub == nil {
|
||||
fmt.Println("❌ PubSub not available - cannot send message")
|
||||
return
|
||||
}
|
||||
|
||||
// Create a human-authored message
|
||||
messageData := map[string]interface{}{
|
||||
"type": "human_message",
|
||||
"author": "human",
|
||||
"node_id": ti.services.Node.ID().ShortString(),
|
||||
"agent_id": ti.services.Config.Agent.ID,
|
||||
"role": ti.services.Config.Agent.Role,
|
||||
"message": message,
|
||||
"timestamp": time.Now().Unix(),
|
||||
}
|
||||
|
||||
// Send to coordination channel
|
||||
if err := ti.services.PubSub.PublishBzzzMessage("coordination", messageData); err != nil {
|
||||
fmt.Printf("❌ Failed to send message: %v\n", err)
|
||||
} else {
|
||||
fmt.Printf("📤 Message sent to coordination channel\n")
|
||||
fmt.Printf("💬 \"%s\"\n", message)
|
||||
}
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
|
||||
func (ti *TerminalInterface) getServiceStatus(serviceName string, available bool) string {
|
||||
if available {
|
||||
return "✅ Active"
|
||||
}
|
||||
return "❌ Inactive"
|
||||
}
|
||||
|
||||
func (ti *TerminalInterface) boolToStatus(b bool) string {
|
||||
if b {
|
||||
return "Healthy"
|
||||
}
|
||||
return "Unhealthy"
|
||||
}
|
||||
|
||||
// IsRunning returns whether the terminal interface is running
|
||||
func (ti *TerminalInterface) IsRunning() bool {
|
||||
return ti.running
|
||||
}
|
||||
|
||||
// GetServices returns the runtime services
|
||||
func (ti *TerminalInterface) GetServices() *runtime.RuntimeServices {
|
||||
return ti.services
|
||||
}
|
||||
Reference in New Issue
Block a user