mirror of
https://github.com/YspCoder/clawgo.git
synced 2026-04-13 05:37:29 +08:00
persist node state to shared file for gateway/cli visibility
This commit is contained in:
@@ -88,6 +88,7 @@ func NewAgentLoop(cfg *config.Config, msgBus *bus.MessageBus, provider providers
|
||||
toolsRegistry.Register(tools.NewProcessTool(processManager))
|
||||
nodesManager := nodes.DefaultManager()
|
||||
nodesManager.SetAuditPath(filepath.Join(workspace, "memory", "nodes-audit.jsonl"))
|
||||
nodesManager.SetStatePath(filepath.Join(workspace, "memory", "nodes-state.json"))
|
||||
nodesManager.Upsert(nodes.NodeInfo{ID: "local", Name: "local", Capabilities: nodes.Capabilities{Run: true, Invoke: true, Model: true, Camera: true, Screen: true, Location: true, Canvas: true}, Models: []string{"local-sim"}, Online: true})
|
||||
nodesManager.RegisterHandler("local", func(req nodes.Request) nodes.Response {
|
||||
switch req.Action {
|
||||
|
||||
@@ -21,6 +21,7 @@ type Manager struct {
|
||||
handlers map[string]Handler
|
||||
ttl time.Duration
|
||||
auditPath string
|
||||
statePath string
|
||||
}
|
||||
|
||||
var defaultManager = NewManager()
|
||||
@@ -39,6 +40,13 @@ func (m *Manager) SetAuditPath(path string) {
|
||||
m.auditPath = strings.TrimSpace(path)
|
||||
}
|
||||
|
||||
func (m *Manager) SetStatePath(path string) {
|
||||
m.mu.Lock()
|
||||
m.statePath = strings.TrimSpace(path)
|
||||
m.mu.Unlock()
|
||||
m.loadState()
|
||||
}
|
||||
|
||||
func (m *Manager) Upsert(info NodeInfo) {
|
||||
m.mu.Lock()
|
||||
now := time.Now().UTC()
|
||||
@@ -60,15 +68,21 @@ func (m *Manager) Upsert(info NodeInfo) {
|
||||
}
|
||||
m.nodes[info.ID] = info
|
||||
m.mu.Unlock()
|
||||
m.saveState()
|
||||
m.appendAudit("upsert", info.ID, map[string]interface{}{"existed": existed, "endpoint": info.Endpoint, "version": info.Version})
|
||||
}
|
||||
|
||||
func (m *Manager) MarkOffline(id string) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
changed := false
|
||||
if n, ok := m.nodes[id]; ok {
|
||||
n.Online = false
|
||||
m.nodes[id] = n
|
||||
changed = true
|
||||
}
|
||||
m.mu.Unlock()
|
||||
if changed {
|
||||
m.saveState()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,6 +222,9 @@ func (m *Manager) reaperLoop() {
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
if len(offlined) > 0 {
|
||||
m.saveState()
|
||||
}
|
||||
for _, id := range offlined {
|
||||
m.appendAudit("offline_ttl", id, nil)
|
||||
}
|
||||
@@ -234,3 +251,47 @@ func (m *Manager) appendAudit(event, nodeID string, data map[string]interface{})
|
||||
defer f.Close()
|
||||
_, _ = f.Write(append(b, '\n'))
|
||||
}
|
||||
|
||||
func (m *Manager) saveState() {
|
||||
m.mu.RLock()
|
||||
path := m.statePath
|
||||
items := make([]NodeInfo, 0, len(m.nodes))
|
||||
for _, n := range m.nodes {
|
||||
items = append(items, n)
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
if strings.TrimSpace(path) == "" {
|
||||
return
|
||||
}
|
||||
_ = os.MkdirAll(filepath.Dir(path), 0755)
|
||||
b, err := json.MarshalIndent(items, "", " ")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_ = os.WriteFile(path, b, 0644)
|
||||
}
|
||||
|
||||
func (m *Manager) loadState() {
|
||||
m.mu.RLock()
|
||||
path := m.statePath
|
||||
m.mu.RUnlock()
|
||||
if strings.TrimSpace(path) == "" {
|
||||
return
|
||||
}
|
||||
b, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var items []NodeInfo
|
||||
if err := json.Unmarshal(b, &items); err != nil {
|
||||
return
|
||||
}
|
||||
m.mu.Lock()
|
||||
for _, n := range items {
|
||||
if strings.TrimSpace(n.ID) == "" {
|
||||
continue
|
||||
}
|
||||
m.nodes[n.ID] = n
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user