package main import ( "encoding/json" "net/http" "time" ) const histMax = 360 // 30 minutes at one sample per 5 seconds type histPoint struct { TS int64 `json:"ts"` CPU float64 `json:"cpu"` Mem float64 `json:"mem"` // used % GPU float64 `json:"gpu"` } type statBuf struct { latest json.RawMessage hist []histPoint at time.Time inst map[string]bool // installed models (normalised names) loaded map[string]bool // models currently in memory memAvail float64 mode string // owner guard mode reported by the machine ("" from older agents = available) kind string // why it is not available: desktop | gpu | cooldown | paused (no program names) retryAt time.Time // estimated time it may be available again; zero = unknown modules map[string]bool // AI modules installed on the machine (capability ids) } // recordStats stores an agent's latest sample (+ short history) and pushes it to every open dashboard. func (h *Hub) recordStats(name string, raw json.RawMessage) { var p struct { TS int64 `json:"ts"` CPUPct float64 `json:"cpu_pct"` MemTotal float64 `json:"mem_total"` MemAvail float64 `json:"mem_avail"` GPU *struct { Util float64 `json:"util"` } `json:"gpu"` Mode string `json:"mode"` Modules []string `json:"modules"` ModeKind string `json:"mode_kind"` ModeRetry int64 `json:"mode_retry_s"` Installed []struct { Name string `json:"name"` } `json:"installed"` LoadedAll []struct { Name string `json:"name"` } `json:"loaded_models"` Ollama struct { Loaded []struct { Name string `json:"name"` } `json:"loaded"` } `json:"ollama"` } if json.Unmarshal(raw, &p) != nil || p.TS == 0 { return } pt := histPoint{TS: p.TS, CPU: p.CPUPct} if p.MemTotal > 0 { pt.Mem = 100 * (1 - p.MemAvail/p.MemTotal) } if p.GPU != nil { pt.GPU = p.GPU.Util } h.mu.Lock() b := h.stats[name] if b == nil { b = &statBuf{} h.stats[name] = b } b.latest = raw b.at = time.Now() b.memAvail = p.MemAvail b.mode = p.Mode b.kind, b.retryAt = p.ModeKind, time.Time{} if p.ModeRetry > 0 { b.retryAt = b.at.Add(time.Duration(p.ModeRetry) * time.Second) } b.modules = map[string]bool{} for _, id := range p.Modules { b.modules[id] = true } b.inst = map[string]bool{} for _, m := range p.Installed { b.inst[m.Name] = true } b.loaded = map[string]bool{} for _, m := range p.Ollama.Loaded { b.loaded[m.Name] = true } for _, m := range p.LoadedAll { b.loaded[m.Name] = true } b.hist = append(b.hist, pt) if len(b.hist) > histMax { b.hist = b.hist[len(b.hist)-histMax:] } var us []*conn for u := range h.uis { us = append(us, u) } h.mu.Unlock() for _, u := range us { u.send(Msg{T: "stats", Agent: name, Data: raw}) } } // sendStatsSnapshot gives a newly opened dashboard the latest sample and history of every machine. func (h *Hub) sendStatsSnapshot(c *conn) { type snap struct { name string latest json.RawMessage hist []histPoint } h.mu.Lock() var snaps []snap for n, b := range h.stats { snaps = append(snaps, snap{n, b.latest, append([]histPoint(nil), b.hist...)}) } h.mu.Unlock() for _, s := range snaps { hb, _ := json.Marshal(s.hist) c.send(Msg{T: "hist", Agent: s.name, Data: hb}) c.send(Msg{T: "stats", Agent: s.name, Data: s.latest}) } } // adminStats prints the latest sample of every machine (local admin socket only). func (s *Server) adminStats(w http.ResponseWriter, r *http.Request) { s.hub.mu.Lock() out := map[string]json.RawMessage{} for n, b := range s.hub.stats { out[n] = b.latest } s.hub.mu.Unlock() w.Header().Set("Content-Type", "application/json") e := json.NewEncoder(w) e.SetIndent("", " ") e.Encode(out) } // waitS is the estimated seconds until this machine may be available again, 0 when unknown. func (b *statBuf) waitS() int { if b.retryAt.IsZero() { return 0 } if w := int(time.Until(b.retryAt).Seconds()) + 1; w > 1 { return w } return 1 }