 9bdcbe0447
			
		
	
	9bdcbe0447
	
	
	
		
			
			Major integrations and fixes: - Added BACKBEAT SDK integration for P2P operation timing - Implemented beat-aware status tracking for distributed operations - Added Docker secrets support for secure license management - Resolved KACHING license validation via HTTPS/TLS - Updated docker-compose configuration for clean stack deployment - Disabled rollback policies to prevent deployment failures - Added license credential storage (CHORUS-DEV-MULTI-001) Technical improvements: - BACKBEAT P2P operation tracking with phase management - Enhanced configuration system with file-based secrets - Improved error handling for license validation - Clean separation of KACHING and CHORUS deployment stacks 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			214 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			214 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package agent
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"fmt"
 | |
| 	"net/http"
 | |
| 	"time"
 | |
| 
 | |
| 	"chorus/internal/config"
 | |
| 	"chorus/internal/logging"
 | |
| )
 | |
| 
 | |
| // Agent represents a CHORUS agent instance
 | |
| type Agent struct {
 | |
| 	id     string
 | |
| 	config *config.Config
 | |
| 	logger logging.Logger
 | |
| 	
 | |
| 	// Services
 | |
| 	apiServer *http.Server
 | |
| 	healthServer *http.Server
 | |
| 	
 | |
| 	// P2P components (to be implemented)
 | |
| 	// p2pHost     host.Host
 | |
| 	// dht         *dht.DHT  
 | |
| 	// pubsub      *pubsub.PubSub
 | |
| }
 | |
| 
 | |
| // New creates a new CHORUS agent
 | |
| func New(ctx context.Context, cfg *config.Config, logger logging.Logger) (*Agent, error) {
 | |
| 	agent := &Agent{
 | |
| 		id:     cfg.Agent.ID,
 | |
| 		config: cfg,
 | |
| 		logger: logger,
 | |
| 	}
 | |
| 	
 | |
| 	// Initialize HTTP servers
 | |
| 	if err := agent.initHTTPServers(); err != nil {
 | |
| 		return nil, fmt.Errorf("failed to initialize HTTP servers: %w", err)
 | |
| 	}
 | |
| 	
 | |
| 	// TODO: Initialize P2P components
 | |
| 	// TODO: Initialize task coordination
 | |
| 	// TODO: Initialize AI integration
 | |
| 	
 | |
| 	return agent, nil
 | |
| }
 | |
| 
 | |
| // Start starts all agent services
 | |
| func (a *Agent) Start() error {
 | |
| 	a.logger.Info("🚀 Starting CHORUS agent services...")
 | |
| 	
 | |
| 	// Start API server
 | |
| 	go func() {
 | |
| 		a.logger.Info("🌐 Starting API server on :%d", a.config.Network.APIPort)
 | |
| 		if err := a.apiServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
 | |
| 			a.logger.Error("❌ API server error: %v", err)
 | |
| 		}
 | |
| 	}()
 | |
| 	
 | |
| 	// Start health server
 | |
| 	go func() {
 | |
| 		a.logger.Info("🏥 Starting health server on :%d", a.config.Network.HealthPort)
 | |
| 		if err := a.healthServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
 | |
| 			a.logger.Error("❌ Health server error: %v", err)
 | |
| 		}
 | |
| 	}()
 | |
| 	
 | |
| 	// TODO: Start P2P services
 | |
| 	// TODO: Start task coordination
 | |
| 	// TODO: Connect to DHT network
 | |
| 	
 | |
| 	a.logger.Info("✅ All CHORUS agent services started")
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // Stop gracefully stops all agent services
 | |
| func (a *Agent) Stop(ctx context.Context) error {
 | |
| 	a.logger.Info("🛑 Stopping CHORUS agent services...")
 | |
| 	
 | |
| 	// Stop HTTP servers
 | |
| 	if err := a.apiServer.Shutdown(ctx); err != nil {
 | |
| 		a.logger.Error("⚠️ Error stopping API server: %v", err)
 | |
| 	}
 | |
| 	
 | |
| 	if err := a.healthServer.Shutdown(ctx); err != nil {
 | |
| 		a.logger.Error("⚠️ Error stopping health server: %v", err)
 | |
| 	}
 | |
| 	
 | |
| 	// TODO: Stop P2P services
 | |
| 	// TODO: Stop task coordination
 | |
| 	// TODO: Disconnect from DHT network
 | |
| 	
 | |
| 	a.logger.Info("✅ CHORUS agent services stopped")
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // ID returns the agent ID
 | |
| func (a *Agent) ID() string {
 | |
| 	return a.id
 | |
| }
 | |
| 
 | |
| // P2PAddress returns the P2P address (placeholder)
 | |
| func (a *Agent) P2PAddress() string {
 | |
| 	// TODO: Return actual P2P address when P2P is implemented
 | |
| 	return fmt.Sprintf("/ip4/%s/tcp/%d/p2p/%s", a.config.Network.BindAddr, a.config.Network.P2PPort, a.id)
 | |
| }
 | |
| 
 | |
| // initHTTPServers initializes the HTTP servers for API and health endpoints
 | |
| func (a *Agent) initHTTPServers() error {
 | |
| 	// API server
 | |
| 	apiMux := http.NewServeMux()
 | |
| 	apiMux.HandleFunc("/", a.handleRoot)
 | |
| 	apiMux.HandleFunc("/agent/info", a.handleAgentInfo)
 | |
| 	apiMux.HandleFunc("/agent/tasks", a.handleTasks)
 | |
| 	
 | |
| 	a.apiServer = &http.Server{
 | |
| 		Addr:         fmt.Sprintf("%s:%d", a.config.Network.BindAddr, a.config.Network.APIPort),
 | |
| 		Handler:      apiMux,
 | |
| 		ReadTimeout:  15 * time.Second,
 | |
| 		WriteTimeout: 15 * time.Second,
 | |
| 		IdleTimeout:  60 * time.Second,
 | |
| 	}
 | |
| 	
 | |
| 	// Health server
 | |
| 	healthMux := http.NewServeMux()
 | |
| 	healthMux.HandleFunc("/health", a.handleHealth)
 | |
| 	healthMux.HandleFunc("/health/ready", a.handleReady)
 | |
| 	healthMux.HandleFunc("/health/live", a.handleLive)
 | |
| 	
 | |
| 	a.healthServer = &http.Server{
 | |
| 		Addr:         fmt.Sprintf("%s:%d", a.config.Network.BindAddr, a.config.Network.HealthPort),
 | |
| 		Handler:      healthMux,
 | |
| 		ReadTimeout:  5 * time.Second,
 | |
| 		WriteTimeout: 5 * time.Second,
 | |
| 		IdleTimeout:  30 * time.Second,
 | |
| 	}
 | |
| 	
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // HTTP handler implementations
 | |
| 
 | |
| func (a *Agent) handleRoot(w http.ResponseWriter, r *http.Request) {
 | |
| 	response := map[string]interface{}{
 | |
| 		"service": "CHORUS",
 | |
| 		"version": "0.1.0-dev",
 | |
| 		"agent_id": a.id,
 | |
| 		"status": "running",
 | |
| 		"endpoints": map[string]string{
 | |
| 			"agent_info": "/agent/info",
 | |
| 			"tasks": "/agent/tasks",
 | |
| 			"health": fmt.Sprintf("http://localhost:%d/health", a.config.Network.HealthPort),
 | |
| 		},
 | |
| 	}
 | |
| 	
 | |
| 	w.Header().Set("Content-Type", "application/json")
 | |
| 	w.WriteHeader(http.StatusOK)
 | |
| 	
 | |
| 	// Simple JSON response without external dependency
 | |
| 	fmt.Fprintf(w, `{
 | |
| 		"service": "%s",
 | |
| 		"version": "0.1.0-dev",
 | |
| 		"agent_id": "%s",
 | |
| 		"status": "running"
 | |
| 	}`, "CHORUS", a.id)
 | |
| }
 | |
| 
 | |
| func (a *Agent) handleAgentInfo(w http.ResponseWriter, r *http.Request) {
 | |
| 	w.Header().Set("Content-Type", "application/json")
 | |
| 	fmt.Fprintf(w, `{
 | |
| 		"agent_id": "%s",
 | |
| 		"specialization": "%s",
 | |
| 		"max_tasks": %d,
 | |
| 		"capabilities": %q
 | |
| 	}`, a.id, a.config.Agent.Specialization, a.config.Agent.MaxTasks, a.config.Agent.Capabilities)
 | |
| }
 | |
| 
 | |
| func (a *Agent) handleTasks(w http.ResponseWriter, r *http.Request) {
 | |
| 	// TODO: Implement task management
 | |
| 	w.Header().Set("Content-Type", "application/json")
 | |
| 	fmt.Fprintf(w, `{
 | |
| 		"active_tasks": [],
 | |
| 		"completed_tasks": [],
 | |
| 		"available_for_tasks": true
 | |
| 	}`)
 | |
| }
 | |
| 
 | |
| func (a *Agent) handleHealth(w http.ResponseWriter, r *http.Request) {
 | |
| 	w.Header().Set("Content-Type", "application/json")
 | |
| 	w.WriteHeader(http.StatusOK)
 | |
| 	fmt.Fprintf(w, `{
 | |
| 		"status": "healthy",
 | |
| 		"agent_id": "%s",
 | |
| 		"timestamp": "%s",
 | |
| 		"checks": {
 | |
| 			"license": "valid",
 | |
| 			"api_server": "running",
 | |
| 			"p2p": "not_implemented"
 | |
| 		}
 | |
| 	}`, a.id, time.Now().UTC().Format(time.RFC3339))
 | |
| }
 | |
| 
 | |
| func (a *Agent) handleReady(w http.ResponseWriter, r *http.Request) {
 | |
| 	// Kubernetes readiness probe
 | |
| 	w.WriteHeader(http.StatusOK)
 | |
| 	fmt.Fprint(w, "ready")
 | |
| }
 | |
| 
 | |
| func (a *Agent) handleLive(w http.ResponseWriter, r *http.Request) {
 | |
| 	// Kubernetes liveness probe
 | |
| 	w.WriteHeader(http.StatusOK)
 | |
| 	fmt.Fprint(w, "live")
 | |
| } |