package api import ( "fmt" "os" "strings" "sync" "time" ) type AgentSession struct { ID string `json:"id"` Type string `json:"type"` PID int `json:"pid"` Command string `json:"command"` StartedAt string `json:"started_at"` Status string `json:"status"` Output string `json:"output,omitempty"` Cwd string `json:"cwd,omitempty"` } type AgentSessionTracker struct { mu sync.RWMutex sessions map[string]*AgentSession } func NewAgentSessionTracker() *AgentSessionTracker { return &AgentSessionTracker{ sessions: make(map[string]*AgentSession), } } func (t *AgentSessionTracker) Discover() []AgentSession { t.mu.Lock() defer t.mu.Unlock() activePIDs := make(map[int]bool) for _, s := range t.sessions { activePIDs[s.PID] = true } for _, name := range []string{"crush", "claude"} { pids := findProcessesByName(name) for _, pid := range pids { if !activePIDs[pid] { session := &AgentSession{ ID: fmt.Sprintf("%s-%d-%d", name, pid, time.Now().UnixMilli()), Type: name, PID: pid, Command: getProcessCommand(pid), StartedAt: time.Now().Format(time.RFC3339), Status: "running", } t.sessions[session.ID] = session } } } var result []AgentSession for _, s := range t.sessions { if s.Status == "running" { if !isProcessAlive(s.PID) { s.Status = "completed" } } result = append(result, *s) } return result } func (t *AgentSessionTracker) Get(id string) *AgentSession { t.mu.RLock() defer t.mu.RUnlock() s, ok := t.sessions[id] if !ok { return nil } snapshot := *s return &snapshot } func findProcessesByName(name string) []int { data, err := os.ReadFile("/proc/" + name + "/stat") _ = data _ = err var pids []int entries, err := os.ReadDir("/proc") if err != nil { return pids } for _, entry := range entries { if !entry.IsDir() { continue } var pid int if _, err := fmt.Sscanf(entry.Name(), "%d", &pid); err != nil { continue } if pid <= 0 || pid == os.Getpid() { continue } cmdline, err := os.ReadFile(fmt.Sprintf("/proc/%d/cmdline", pid)) if err != nil { continue } cmdStr := string(cmdline) if strings.Contains(cmdStr, name) { pids = append(pids, pid) } } return pids } func getProcessCommand(pid int) string { out, err := os.ReadFile(fmt.Sprintf("/proc/%d/cmdline", pid)) if err != nil { return "" } return strings.ReplaceAll(string(out), "\x00", " ") } func isProcessAlive(pid int) bool { _, err := os.Stat(fmt.Sprintf("/proc/%d", pid)) return err == nil }