feat(nest): POST/GET nest messages, Jev-first tagging, event-driven crow response (#10)

This commit is contained in:
Smokey committed 2026-10-28 03:34:37 -06:00
1 parent ba5dbbc7ae
commit e4e4d12dea
5 files changed
+281 -1

No files matched your search

+18 -1
View File
@@ -32,6 +32,7 @@ type Server struct {
approver *WebApprover
hub *Hub
nests *NestStore
msgs *MessageStore
ctx context.Context // cancelled on shutdown; lists and streams end with it
beat time.Duration
}
@@ -40,6 +41,9 @@ func NewServer(ctx context.Context, p *Pipeline, approver *WebApprover, nests *N
return &Server{p: p, approver: approver, hub: NewHub(), nests: nests, ctx: ctx, beat: heartbeat}
}
// WithMessages attaches the nest conversation log (chatroom path).
func (s *Server) WithMessages(m *MessageStore) *Server { s.msgs = m; return s }
func (s *Server) Handler() http.Handler {
mux := http.NewServeMux()
mux.HandleFunc("POST /api/lists", s.postList)
@@ -49,6 +53,8 @@ func (s *Server) Handler() http.Handler {
mux.HandleFunc("GET /api/crows", s.crows)
mux.HandleFunc("GET /api/nests", s.getNests)
mux.HandleFunc("POST /api/nests", s.postNest)
mux.HandleFunc("GET /api/nests/{nest_id}/messages", s.getNestMessages)
mux.HandleFunc("POST /api/nests/{nest_id}/messages", s.postNestMessage)
if st, err := os.Stat(webDir); err == nil && st.IsDir() {
mux.Handle("GET /", http.FileServer(http.Dir(webDir)))
} else {
@@ -73,6 +79,8 @@ func cmdServe(args []string) {
defer flog.Close()
nests, err := LoadNests(filepath.Join(dataDir, "nests.json"))
must(err)
msgs, err := LoadMessageStore(filepath.Join(dataDir, "messages"))
must(err)
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
defer stop()
@@ -84,7 +92,7 @@ func cmdServe(args []string) {
if jev != nil {
fmt.Printf("Sentry triage: %s, LLM fallback\n", jev.Name())
}
s := NewServer(ctx, p, approver, nests)
s := NewServer(ctx, p, approver, nests).WithMessages(msgs)
srv := &http.Server{Addr: *addr, Handler: s.Handler(), ReadHeaderTimeout: 10 * time.Second}
go func() {
@@ -190,6 +198,15 @@ func newListID() string {
func (s *Server) events(w http.ResponseWriter, r *http.Request) {
listID := r.URL.Query().Get("list_id")
nestID := r.URL.Query().Get("nest_id")
if listID == "" && nestID != "" {
if _, found := s.nests.Get(nestID); !found {
httpError(w, http.StatusNotFound, "no such nest_id")
return
}
s.hub.OpenNest(nestID)
listID = nestID
}
lastID, _ := strconv.Atoi(r.Header.Get("Last-Event-ID"))
past, ch, ok := s.hub.Subscribe(listID, lastID)
if !ok {