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 // GET /api/agent/departments — list departments with status
import { execSync } from "child_process"; import { execSync } from "child_process";
import { readdirSync, statSync } from "fs";
import { join } from "path";
import { DEPARTMENTS } from "@/lib/constants"; import { DEPARTMENTS } from "@/lib/constants";
const MEMORY_DIR = "/home/sscoble/.openclaw/workspace/memory";
export async function GET() { export async function GET() {
try { 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 departments = DEPARTMENTS.map((d) => {
const name = d.name.replace("@", "");
let status = "idle"; let status = "idle";
let lastSession = null; let lastSession = globalLastSession;
let memorySize = null; let memorySize = globalMemorySize;
try { 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 // Check if the agent's process is running
const procCheck = execSync( const procCheck = execSync(
`pgrep -f 'openclaw.*${name}' || true`, `pgrep -f 'openclaw.*${name}' || true`,
@ -26,24 +42,21 @@ export async function GET() {
status = "running"; status = "running";
} }
// Check memory file size // Check for per-agent memory file
try { try {
const stat = execSync( const agentFiles = readdirSync(MEMORY_DIR)
`stat --format='%Y %s' /home/sscoble/.openclaw/workspace/memory/${name}-*.md 2>/dev/null || true`, .filter((f) => f.startsWith(name) && f.endsWith(".md"));
{ encoding: "utf-8", timeout: 2000 } if (agentFiles.length > 0) {
); const latest = join(MEMORY_DIR, agentFiles.sort().reverse()[0]);
if (stat.trim()) { const st = statSync(latest);
const parts = stat.trim().split(" "); lastSession = new Date(st.mtime).toISOString();
const timestamp = parseInt(parts[0]) * 1000; memorySize = formatBytes(st.size);
const sizeBytes = parseInt(parts[1]);
lastSession = new Date(timestamp).toISOString();
memorySize = formatBytes(sizeBytes);
} }
} catch { } catch {
// no memory file // no per-agent memory file, use global
} }
} catch { } catch {
// process check failed // process check failed, leave status as idle
} }
return { 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-sm font-medium text-gray-200">{agent.name}</p>
<p className="text-xs text-gray-500 truncate">{agent.role}</p> <p className="text-xs text-gray-500 truncate">{agent.role}</p>
</div> </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 && ( {agent.lastSession && (
<p> <p className="mt-0.5">
{new Date(agent.lastSession).toLocaleDateString("en-US", { {new Date(agent.lastSession).toLocaleDateString("en-US", {
month: "short", month: "short",
day: "numeric", day: "numeric",
@ -27,6 +32,9 @@ export default function AgentStatus({ agent }) {
</p> </p>
)} )}
{agent.memorySize && <p className="mt-0.5">{agent.memorySize}</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>
</div> </div>
); );