heartbeat.go
59 lines1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
//go:build cgo
package commands
import (
"fmt"
"os/exec"
"strings"
"congo.gg/dev/models"
)
// Heartbeat is the idempotent system evaluator. It checks the state
// of the system, writes a heartbeat to the DB, and corrects any drift.
// Called periodically by the persistent Claude agent.
func Heartbeat() {
var status []string
// Check containers
if out, err := exec.Command("docker", "ps", "--format", "{{.Names}}: {{.Status}}").Output(); err == nil {
lines := strings.Split(strings.TrimSpace(string(out)), "\n")
healthy := 0
for _, l := range lines {
if strings.Contains(l, "healthy") || strings.Contains(l, "Up") {
healthy++
}
}
status = append(status, fmt.Sprintf("containers: %d/%d healthy", healthy, len(lines)))
}
// Check disk
if out, err := exec.Command("df", "-h", "/").Output(); err == nil {
lines := strings.Split(string(out), "\n")
if len(lines) > 1 {
fields := strings.Fields(lines[1])
if len(fields) >= 5 {
status = append(status, fmt.Sprintf("disk: %s used (%s)", fields[4], fields[2]))
}
}
}
// Check if claude is running in code-server
if out, err := exec.Command("docker", "exec", "congo-dev-coder", "pgrep", "-f", "claude").Output(); err == nil && len(strings.TrimSpace(string(out))) > 0 {
status = append(status, "agent: running")
} else {
status = append(status, "agent: not running")
}
summary := strings.Join(status, ", ")
// Write heartbeat to DB
models.LogEntries.Insert(&models.LogEntry{
Type: "heartbeat",
Summary: summary,
Author: "system",
})
fmt.Println(summary)
}