Add home-security-agent with PostgreSQL persistence for dashboard.
The autonomous agent writes all observations to agent.* tables consumed by Homelab Command on port 8765. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
"""SQLite — geheugen van de agent (dedupe, incidenten)."""
|
||||
import json
|
||||
import sqlite3
|
||||
from datetime import datetime, timedelta
|
||||
from pathlib import Path
|
||||
|
||||
DB_PATH = Path(__file__).resolve().parent.parent / "data" / "agent.db"
|
||||
|
||||
|
||||
def connect():
|
||||
DB_PATH.parent.mkdir(parents=True, exist_ok=True)
|
||||
conn = sqlite3.connect(DB_PATH)
|
||||
conn.row_factory = sqlite3.Row
|
||||
conn.executescript(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS incidents (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
fingerprint TEXT NOT NULL,
|
||||
severity TEXT NOT NULL,
|
||||
title TEXT NOT NULL,
|
||||
body TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL,
|
||||
notified INTEGER DEFAULT 0
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_fp ON incidents(fingerprint);
|
||||
CREATE TABLE IF NOT EXISTS runs (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
started_at TEXT NOT NULL,
|
||||
summary TEXT,
|
||||
raw_observations TEXT
|
||||
);
|
||||
"""
|
||||
)
|
||||
return conn
|
||||
|
||||
|
||||
def was_notified_recently(fingerprint: str, minutes: int) -> bool:
|
||||
since = (datetime.utcnow() - timedelta(minutes=minutes)).isoformat()
|
||||
conn = connect()
|
||||
row = conn.execute(
|
||||
"SELECT 1 FROM incidents WHERE fingerprint=? AND notified=1 AND created_at>?",
|
||||
(fingerprint, since),
|
||||
).fetchone()
|
||||
conn.close()
|
||||
return row is not None
|
||||
|
||||
|
||||
def record_incident(fingerprint: str, severity: str, title: str, body: str, notified: bool):
|
||||
conn = connect()
|
||||
conn.execute(
|
||||
"INSERT INTO incidents (fingerprint, severity, title, body, created_at, notified) VALUES (?,?,?,?,?,?)",
|
||||
(fingerprint, severity, title, body, datetime.utcnow().isoformat(), 1 if notified else 0),
|
||||
)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
|
||||
def recent_incidents(limit: int = 10) -> list[dict]:
|
||||
conn = connect()
|
||||
rows = conn.execute(
|
||||
"SELECT severity, title, body, created_at FROM incidents ORDER BY id DESC LIMIT ?",
|
||||
(limit,),
|
||||
).fetchall()
|
||||
conn.close()
|
||||
return [dict(r) for r in rows]
|
||||
|
||||
|
||||
def log_run(summary: str, observations: dict):
|
||||
conn = connect()
|
||||
conn.execute(
|
||||
"INSERT INTO runs (started_at, summary, raw_observations) VALUES (?,?,?)",
|
||||
(datetime.utcnow().isoformat(), summary, json.dumps(observations, default=str)),
|
||||
)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
Reference in New Issue
Block a user