Initial CHORUS project setup
🎭 CHORUS - Container-First P2P Task Coordination System - Docker-first architecture designed from ground up - Environment variable-based configuration (no config files) - Structured logging to stdout/stderr for container runtimes - License validation required for operation - Clean separation from BZZZ legacy systemd approach Core features implemented: - Container-optimized logging system - Environment-based configuration management - License validation with KACHING integration - Basic HTTP API and health endpoints - Docker build and deployment configuration Ready for P2P protocol development and AI integration. 🤖 Generated with Claude Code
This commit is contained in:
214
internal/agent/agent.go
Normal file
214
internal/agent/agent.go
Normal file
@@ -0,0 +1,214 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"chorus.services/chorus/internal/config"
|
||||
"chorus.services/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")
|
||||
}
|
||||
Reference in New Issue
Block a user