Files
hermes-dashboard/services/worker.py

79 lines
2.6 KiB
Python

"""Worker service — process scanning and SOUL.md reading."""
import logging
import os
from typing import Any
import psutil
import config
logger = logging.getLogger(__name__)
def get_running_processes() -> list[dict[str, Any]]:
"""Scan for opencode worker processes (cmdline contains both 'opencode' and 'run')."""
processes: list[dict[str, Any]] = []
try:
for proc in psutil.process_iter(["pid", "create_time", "cmdline"]):
try:
cmdline = proc.info.get("cmdline") or []
cmdline_str = " ".join(cmdline)
if "opencode" in cmdline_str and "run" in cmdline_str:
# Truncate cmdline to 200 chars
if len(cmdline_str) > 200:
cmdline_str = cmdline_str[:200] + "..."
# Calculate elapsed time
create_time = proc.info.get("create_time", 0)
processes.append({
"pid": proc.info["pid"],
"cmdline": cmdline_str,
"create_time": create_time,
})
except (psutil.NoSuchProcess, psutil.AccessDenied):
continue
except Exception as exc:
logger.exception("get_running_processes failed")
return processes
def get_current_task() -> dict[str, Any] | None:
"""Find the currently running task (status=running) from kanban DB."""
try:
from extensions import get_db
db = get_db()
row = db.execute(
"SELECT * FROM tasks WHERE status = 'running' ORDER BY started_at DESC LIMIT 1"
).fetchone()
if row:
return dict(row)
except Exception as exc:
logger.debug("get_current_task failed: %s", exc)
return None
def get_soul_excerpt(max_chars: int = 2000) -> str:
"""Read first max_chars from SOUL.md."""
try:
with open(config.SOUL_MD_PATH, "r", encoding="utf-8") as f:
content = f.read(max_chars)
return content
except (FileNotFoundError, PermissionError, OSError) as exc:
logger.debug("SOUL.md read failed: %s", exc)
return f"(SOUL.md not available: {exc})"
def get_worker_status() -> dict[str, Any]:
"""Aggregate worker status: current task, processes, soul excerpt."""
return {
"current_task": get_current_task(),
"processes": get_running_processes(),
"soul_excerpt": get_soul_excerpt(500),
}
def get_soul_full() -> dict[str, str]:
"""Return first 2000 chars of SOUL.md."""
return {"content": get_soul_excerpt(2000)}