Files
timmy-config/bin/timmy-status.sh

183 lines
5.6 KiB
Bash
Raw Permalink Normal View History

#!/usr/bin/env bash
# ── Timmy Status Sidebar ───────────────────────────────────────────────
# Compact current-state view for the local Hermes + Timmy workflow.
# ───────────────────────────────────────────────────────────────────────
set -euo pipefail
resolve_gitea_url() {
if [ -n "${GITEA_URL:-}" ]; then
printf '%s\n' "${GITEA_URL%/}"
return 0
fi
if [ -f "$HOME/.hermes/gitea_api" ]; then
python3 - "$HOME/.hermes/gitea_api" <<'PY'
from pathlib import Path
import sys
raw = Path(sys.argv[1]).read_text().strip().rstrip("/")
print(raw[:-7] if raw.endswith("/api/v1") else raw)
PY
return 0
fi
if [ -f "$HOME/.config/gitea/base-url" ]; then
tr -d '[:space:]' < "$HOME/.config/gitea/base-url"
return 0
fi
echo "ERROR: set GITEA_URL or create ~/.hermes/gitea_api" >&2
return 1
}
resolve_ops_token() {
local token_file
for token_file in \
"$HOME/.config/gitea/timmy-token" \
"$HOME/.hermes/gitea_token_vps" \
"$HOME/.hermes/gitea_token_timmy"; do
if [ -f "$token_file" ]; then
tr -d '[:space:]' < "$token_file"
return 0
fi
done
return 1
}
GITEA_URL="$(resolve_gitea_url)"
CORE_REPOS="${CORE_REPOS:-Timmy_Foundation/the-nexus Timmy_Foundation/timmy-home Timmy_Foundation/timmy-config Timmy_Foundation/hermes-agent}"
TOKEN="$(resolve_ops_token || true)"
[ -z "$TOKEN" ] && echo "WARN: no approved Timmy Gitea token found; status sidebar will use unauthenticated API calls" >&2
B='\033[1m'
D='\033[2m'
R='\033[0m'
G='\033[32m'
Y='\033[33m'
RD='\033[31m'
C='\033[36m'
COLS=$(tput cols 2>/dev/null || echo 48)
hr() { printf "${D}"; printf '─%.0s' $(seq 1 "$COLS"); printf "${R}\n"; }
while true; do
clear
echo -e "${B}${C} TIMMY STATUS${R} ${D}$(date '+%H:%M:%S')${R}"
hr
python3 - "$HOME/.timmy" "$HOME/.hermes" <<'PY'
import json
import sys
from pathlib import Path
timmy = Path(sys.argv[1])
hermes = Path(sys.argv[2])
last_tick = timmy / "heartbeat" / "last_tick.json"
model_health = hermes / "model_health.json"
checkpoint = timmy / "twitter-archive" / "checkpoint.json"
if last_tick.exists():
try:
tick = json.loads(last_tick.read_text())
sev = tick.get("decision", {}).get("severity", "?")
tick_id = tick.get("tick_id", "?")
print(f" heartbeat {tick_id} severity={sev}")
except Exception:
print(" heartbeat unreadable")
else:
print(" heartbeat missing")
if model_health.exists():
try:
health = json.loads(model_health.read_text())
provider_ok = health.get("api_responding")
inference_ok = health.get("inference_ok")
models = len(health.get("models_loaded", []) or [])
print(f" model api={provider_ok} inference={inference_ok} models={models}")
except Exception:
print(" model unreadable")
else:
print(" model missing")
if checkpoint.exists():
try:
cp = json.loads(checkpoint.read_text())
print(f" archive batches={cp.get('batches_completed', '?')} next={cp.get('next_offset', '?')} phase={cp.get('phase', '?')}")
except Exception:
print(" archive unreadable")
else:
print(" archive missing")
PY
hr
echo -e " ${B}freshness${R}"
~/.hermes/bin/pipeline-freshness.sh 2>/dev/null | sed 's/^/ /' || echo -e " ${Y}unknown${R}"
hr
echo -e " ${B}review queue${R}"
python3 - "$GITEA_URL" "$TOKEN" "$CORE_REPOS" <<'PY'
import json
import sys
import urllib.request
base = sys.argv[1].rstrip("/")
token = sys.argv[2]
repos = sys.argv[3].split()
headers = {"Authorization": f"token {token}"} if token else {}
count = 0
for repo in repos:
try:
req = urllib.request.Request(f"{base}/api/v1/repos/{repo}/issues?state=open&limit=50&type=pulls", headers=headers)
with urllib.request.urlopen(req, timeout=5) as resp:
items = json.loads(resp.read().decode())
for item in items:
assignees = [a.get("login", "") for a in (item.get("assignees") or [])]
if any(name in assignees for name in ("Timmy", "allegro")):
print(f" {repo.split('/',1)[1]:12s} #{item['number']:<4d} {item['title'][:28]}")
count += 1
if count >= 6:
raise SystemExit
except SystemExit:
break
except Exception:
continue
if count == 0:
print(" (clear)")
PY
hr
echo -e " ${B}unassigned${R}"
python3 - "$GITEA_URL" "$TOKEN" "$CORE_REPOS" <<'PY'
import json
import sys
import urllib.request
base = sys.argv[1].rstrip("/")
token = sys.argv[2]
repos = sys.argv[3].split()
headers = {"Authorization": f"token {token}"} if token else {}
count = 0
for repo in repos:
try:
req = urllib.request.Request(f"{base}/api/v1/repos/{repo}/issues?state=open&limit=50&type=issues", headers=headers)
with urllib.request.urlopen(req, timeout=5) as resp:
items = json.loads(resp.read().decode())
for item in items:
if not item.get("assignees"):
print(f" {repo.split('/',1)[1]:12s} #{item['number']:<4d} {item['title'][:28]}")
count += 1
if count >= 6:
raise SystemExit
except SystemExit:
break
except Exception:
continue
if count == 0:
print(" (none)")
PY
hr
sleep 10
done