Files
claude-code/dashboard/cmd/server/main.go
OpenCode Test ae958528a6 Add Claude integration to dashboard
Add comprehensive Claude Code monitoring and realtime streaming to the K8s dashboard.
Includes API endpoints for health, stats, summary, inventory, and live event streaming.
Frontend provides overview, usage, inventory, debug, and live feed views.
2026-01-03 10:54:48 -08:00

117 lines
3.1 KiB
Go

package main
import (
"embed"
"flag"
"io/fs"
"log"
"net/http"
"os"
"path/filepath"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/go-chi/cors"
"github.com/will/k8s-agent-dashboard/internal/api"
"github.com/will/k8s-agent-dashboard/internal/claude"
"github.com/will/k8s-agent-dashboard/internal/store"
)
//go:embed all:web
var webFS embed.FS
func defaultClaudeDir() string {
home, err := os.UserHomeDir()
if err != nil {
return "/home/will/.claude" // fallback; best-effort
}
return filepath.Join(home, ".claude")
}
func main() {
port := flag.String("port", "8080", "Server port")
dataDir := flag.String("data", "/data", "Data directory for state")
claudeDir := flag.String("claude", defaultClaudeDir(), "Claude Code directory")
flag.Parse()
// Initialize store
s, err := store.New(*dataDir)
if err != nil {
log.Fatalf("Failed to initialize store: %v", err)
}
// Initialize Claude loader
claudeLoader := claude.NewLoader(*claudeDir)
// Initialize event hub
hub := claude.NewEventHub(1000)
// Create router
r := chi.NewRouter()
// Middleware
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)
r.Use(middleware.Compress(5))
r.Use(cors.Handler(cors.Options{
AllowedOrigins: []string{"*"},
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowedHeaders: []string{"Accept", "Content-Type"},
ExposedHeaders: []string{"Link"},
AllowCredentials: false,
MaxAge: 300,
}))
// API routes
r.Route("/api", func(r chi.Router) {
r.Get("/health", api.HealthCheck)
r.Route("/claude", func(r chi.Router) {
r.Get("/health", api.GetClaudeHealth(claudeLoader))
r.Get("/stats", api.GetClaudeStats(claudeLoader))
r.Get("/summary", api.GetClaudeSummary(claudeLoader))
r.Get("/inventory", api.GetClaudeInventory(claudeLoader))
r.Get("/debug/files", api.GetClaudeDebugFiles(claudeLoader))
r.Get("/live/backlog", api.GetClaudeLiveBacklog(claudeLoader))
r.Get("/stream", api.GetClaudeStream(hub))
})
r.Get("/status", api.GetClusterStatus(s))
r.Get("/pending", api.GetPendingActions(s))
r.Post("/pending/{id}/approve", api.ApproveAction(s))
r.Post("/pending/{id}/reject", api.RejectAction(s))
r.Get("/history", api.GetActionHistory(s))
r.Get("/workflows", api.GetWorkflows(s))
r.Post("/workflows/{name}/run", api.RunWorkflow(s))
r.Post("/workflows/{name}/complete", api.CompleteWorkflow(s))
// Webhook endpoints
r.Post("/webhooks/alertmanager", api.AlertmanagerWebhook(s))
})
// Static files
webContent, err := fs.Sub(webFS, "web")
if err != nil {
log.Fatalf("Failed to get web content: %v", err)
}
fileServer := http.FileServer(http.FS(webContent))
r.Handle("/*", fileServer)
// Start server
addr := ":" + *port
if envPort := os.Getenv("PORT"); envPort != "" {
addr = ":" + envPort
}
log.Printf("Starting server on %s", addr)
log.Printf("Data directory: %s", *dataDir)
log.Printf("Claude directory: %s", *claudeDir)
stop := make(chan struct{})
go claude.TailHistoryFile(stop, hub, filepath.Join(*claudeDir, "history.jsonl"))
if err := http.ListenAndServe(addr, r); err != nil {
log.Fatalf("Server failed: %v", err)
}
}