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>
322 lines
9.3 KiB
Go
322 lines
9.3 KiB
Go
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
|
||
} |