Improve agent status dashboard: fix memory file detection, show status text and session count

- Fix departments route: use readdirSync instead of broken glob pattern to find memory files
- Show global memory file info (last modified, size) when no per-agent files exist
- Add status text label (Running/Idle) and session count display to AgentStatus component
- Improve visual layout with fixed-width info column
- Closes #2.
This commit is contained in:
Scott Scoble 2026-04-26 01:09:48 -07:00
parent 1cd5c252b3
commit bbef0ebeed
2 changed files with 45 additions and 24 deletions

View file

@ -1,22 +1,38 @@
// GET /api/agent/departments — list departments with status
import { execSync } from "child_process";
import { readdirSync, statSync } from "fs";
import { join } from "path";
import { DEPARTMENTS } from "@/lib/constants";
const MEMORY_DIR = "/home/sscoble/.openclaw/workspace/memory";
export async function GET() {
try {
// Get info about the most recent daily memory file
let globalLastSession = null;
let globalMemorySize = null;
try {
const files = readdirSync(MEMORY_DIR)
.filter((f) => f.endsWith(".md"))
.sort()
.reverse();
if (files.length > 0) {
const latest = join(MEMORY_DIR, files[0]);
const st = statSync(latest);
globalLastSession = new Date(st.mtime).toISOString();
globalMemorySize = formatBytes(st.size);
}
} catch {
// memory dir not found
}
const departments = DEPARTMENTS.map((d) => {
const name = d.name.replace("@", "");
let status = "idle";
let lastSession = null;
let memorySize = null;
let lastSession = globalLastSession;
let memorySize = globalMemorySize;
try {
// Check for memory files per department
const name = d.name.replace("@", "");
const memPath = `/home/sscoble/.openclaw/workspace/memory/${name}-YYYY-MM-DD.md`;
// Simple check: does a recent memory file exist?
const today = new Date().toISOString().split("T")[0];
const yesterday = new Date(Date.now() - 86400000).toISOString().split("T")[0];
// Check if the agent's process is running
const procCheck = execSync(
`pgrep -f 'openclaw.*${name}' || true`,
@ -26,24 +42,21 @@ export async function GET() {
status = "running";
}
// Check memory file size
// Check for per-agent memory file
try {
const stat = execSync(
`stat --format='%Y %s' /home/sscoble/.openclaw/workspace/memory/${name}-*.md 2>/dev/null || true`,
{ encoding: "utf-8", timeout: 2000 }
);
if (stat.trim()) {
const parts = stat.trim().split(" ");
const timestamp = parseInt(parts[0]) * 1000;
const sizeBytes = parseInt(parts[1]);
lastSession = new Date(timestamp).toISOString();
memorySize = formatBytes(sizeBytes);
const agentFiles = readdirSync(MEMORY_DIR)
.filter((f) => f.startsWith(name) && f.endsWith(".md"));
if (agentFiles.length > 0) {
const latest = join(MEMORY_DIR, agentFiles.sort().reverse()[0]);
const st = statSync(latest);
lastSession = new Date(st.mtime).toISOString();
memorySize = formatBytes(st.size);
}
} catch {
// no memory file
// no per-agent memory file, use global
}
} catch {
// process check failed
// process check failed, leave status as idle
}
return {

View file

@ -15,9 +15,14 @@ export default function AgentStatus({ agent }) {
<p className="text-sm font-medium text-gray-200">{agent.name}</p>
<p className="text-xs text-gray-500 truncate">{agent.role}</p>
</div>
<div className="text-right text-xs text-gray-500 flex-shrink-0">
<div className="text-right text-xs text-gray-500 flex-shrink-0 min-w-[100px]">
{agent.status && (
<p className={`font-medium ${agent.status === "running" ? "text-green-400" : "text-yellow-400"}`}>
{agent.status.charAt(0).toUpperCase() + agent.status.slice(1)}
</p>
)}
{agent.lastSession && (
<p>
<p className="mt-0.5">
{new Date(agent.lastSession).toLocaleDateString("en-US", {
month: "short",
day: "numeric",
@ -27,6 +32,9 @@ export default function AgentStatus({ agent }) {
</p>
)}
{agent.memorySize && <p className="mt-0.5">{agent.memorySize}</p>}
{agent.sessionCount !== undefined && (
<p className="mt-0.5 text-gray-600">{agent.sessionCount} sessions</p>
)}
</div>
</div>
);