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") }