dashboard/AGENTS.md

3.4 KiB

AGENTS.md

Guidance for AI coding agents working in this repository.

What this is

A single-host computer dashboard and task manager: FastAPI (Python ≥ 3.12, managed with uv) backend serving htmx + Chart.js HTML fragments to one browser page. Binds to 127.0.0.1:8501, no auth by design (see the README security section). Tabs: Overview, Disks, Processes, Journal, Services (systemd), Plugins (llama.cpp). Licensed under the Unlicense (see LICENSE).

Commands

uv sync                 # install dependencies
uv run python main.py   # run the server on http://127.0.0.1:8501

There is no test suite. Verify changes with:

uv run python -m compileall -q app
curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8501/api/overview
# ... other endpoints: /api/disks /api/processes /api/journal /api/services
#                      /api/plugins /api/history
grep -c Traceback /tmp/dash.log

Restarting the dev server

The server usually runs detached in the background. To restart it:

PID=$(pgrep -f "python main\.py" | head -1)
[ -n "$PID" ] && kill "$PID"
setsid nohup uv run python main.py > /tmp/dash.log 2>&1 < /dev/null & disown

Never use pkill -f "uv run python main.py" — the pattern also matches the agent's own shell command line and kills the session.

Architecture

  • app/collect/* — collectors (cpu/mem/gpu/disks/procs/net/power) read psutil + sysfs; app/sampling.py runs them every DASH_SAMPLE_INTERVAL (default 2 s) into an in-memory ring buffer (app/state.py).
  • app/routers/* — each tab endpoint is an idempotent GET returning an htmx HTML fragment; templates live in templates/ and self-poll via hx-get + hx-trigger="every Ns" + hx-swap="outerHTML".
  • templates/*.html auto-reload on file change — no restart needed for template-only edits. Python changes require a restart.
  • app/systemd/units.py — systemd unit listing/detail/actions; app/journal.pyjournalctl -o export parser with cursors.
  • app/plugins/base.Plugin + llamacpp plugin (talks to a router-mode llama-server on port 8080).

Conventions

  • No code comments (the codebase has none).
  • basedpyright is configured as linter
  • Match surrounding style; keep functions small and typed where the codebase already is.
  • Keep polling endpoints cheap: collectors may cache lookups (unit names, enabled-state maps, SSID, temperature paths) with short TTLs.

Hard-won pitfalls

  • Jinja autoescape renders &#8595; as literal text — use literal unicode (e.g. ) in templates.
  • journalctl -o export output contains NUL bytes (grep treats it as binary); journalctl rejects negated matches (!/!=) — filter entries in Python instead.
  • psutil gotchas: there is no psutil.AF_INET (use socket); net_if_addrs() / net_if_stats() take no arguments; sensors_battery().power_plugged can be None — use /sys/class/power_supply/* (type/capacity/status/online) for battery + AC state.
  • iw dev <if> link prints SSID: name unquoted; the working regex is SSID:\s+(\S.*) (a $ anchor fails without MULTILINE).
  • /api/history pads series with null for samples missing a key so all series stay aligned with the timestamps — keep that behaviour if you touch it.
  • AMD sysfs: GPU busy/VRAM/temp under /sys/class/drm/card*/device (+ hwmon), CPU temp from the k10temp hwmon (fallback acpitz thermal zone), both in millidegrees.