From 2e2b2187b957e9af78797a67ec5c6874615fae02 Mon Sep 17 00:00:00 2001 From: Peter Stone Date: Sun, 8 Feb 2026 21:35:45 -1000 Subject: Initial project: task model, executor, API server, CLI, storage, reporter Claudomator automation toolkit for Claude Code with: - Task model with YAML parsing, validation, state machine (49 tests, 0 races) - SQLite storage for tasks and executions - Executor pool with bounded concurrency, timeout, cancellation - REST API + WebSocket for mobile PWA integration - Webhook/multi-notifier system - CLI: init, run, serve, list, status commands - Console, JSON, HTML reporters with cost tracking Co-Authored-By: Claude Opus 4.6 --- internal/api/websocket.go | 72 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 internal/api/websocket.go (limited to 'internal/api/websocket.go') diff --git a/internal/api/websocket.go b/internal/api/websocket.go new file mode 100644 index 0000000..6bd8c88 --- /dev/null +++ b/internal/api/websocket.go @@ -0,0 +1,72 @@ +package api + +import ( + "log/slog" + "net/http" + "sync" + + "golang.org/x/net/websocket" +) + +// Hub manages WebSocket connections and broadcasts messages. +type Hub struct { + mu sync.RWMutex + clients map[*websocket.Conn]bool + logger *slog.Logger +} + +func NewHub() *Hub { + return &Hub{ + clients: make(map[*websocket.Conn]bool), + logger: slog.Default(), + } +} + +// Run is a no-op loop kept for future cleanup/heartbeat logic. +func (h *Hub) Run() {} + +func (h *Hub) Register(ws *websocket.Conn) { + h.mu.Lock() + h.clients[ws] = true + h.mu.Unlock() +} + +func (h *Hub) Unregister(ws *websocket.Conn) { + h.mu.Lock() + delete(h.clients, ws) + h.mu.Unlock() +} + +// Broadcast sends a message to all connected WebSocket clients. +func (h *Hub) Broadcast(msg []byte) { + h.mu.RLock() + defer h.mu.RUnlock() + for conn := range h.clients { + if _, err := conn.Write(msg); err != nil { + h.logger.Error("websocket write error", "error", err) + } + } +} + +// ClientCount returns the number of connected clients. +func (h *Hub) ClientCount() int { + h.mu.RLock() + defer h.mu.RUnlock() + return len(h.clients) +} + +func (s *Server) handleWebSocket(w http.ResponseWriter, r *http.Request) { + handler := websocket.Handler(func(ws *websocket.Conn) { + s.hub.Register(ws) + defer s.hub.Unregister(ws) + + // Keep connection alive until client disconnects. + buf := make([]byte, 1024) + for { + if _, err := ws.Read(buf); err != nil { + break + } + } + }) + handler.ServeHTTP(w, r) +} -- cgit v1.2.3