- Reset master to upstream/main (16,697 commits) - Overlay 2,271 local-only files (skills, tools, workspace, configs, apps) - Restore IDENTITY.md and USER.md templates - Build verified, gateway running, Discord working Co-Authored-By: Claude Opus 4.6 <[email protected]>
80 lines
2.2 KiB
Python
80 lines
2.2 KiB
Python
#!/usr/bin/env python3
|
|
import json
|
|
import os
|
|
import sys
|
|
import time
|
|
from pathlib import Path
|
|
|
|
from james.lib.log import get_logger
|
|
|
|
log = get_logger("audit")
|
|
|
|
|
|
def redis_url() -> str:
|
|
url = os.environ.get("REDIS_URL")
|
|
if url:
|
|
return url
|
|
host = os.environ.get("REDIS_HOST", "127.0.0.1")
|
|
port = os.environ.get("REDIS_PORT", "6379")
|
|
password = os.environ.get("REDIS_PASSWORD")
|
|
auth = f":{password}@" if password else ""
|
|
return f"redis://{auth}{host}:{port}/0"
|
|
|
|
|
|
def main() -> int:
|
|
channels = [
|
|
"james.events.system",
|
|
"james.events.mail",
|
|
"james.events.voice",
|
|
"james.decisions",
|
|
"james.actions",
|
|
"james.audit",
|
|
]
|
|
log_path = Path(os.environ.get("JAMES_AUDIT_LOG", "james/audit/audit.log"))
|
|
log_path.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
try:
|
|
import redis
|
|
except Exception as exc: # pragma: no cover - runtime guard
|
|
log.error("redis is required", exc_info=True)
|
|
return 2
|
|
|
|
url = redis_url()
|
|
try:
|
|
r = redis.Redis.from_url(url)
|
|
r.ping()
|
|
except Exception:
|
|
log.error("redis connection failed", exc_info=True)
|
|
return 1
|
|
|
|
pubsub = r.pubsub()
|
|
pubsub.subscribe(*channels)
|
|
log.info("started", channels=channels, log_file=str(log_path))
|
|
|
|
with log_path.open("a", encoding="utf-8") as handle:
|
|
for message in pubsub.listen():
|
|
if message.get("type") != "message":
|
|
continue
|
|
data = message.get("data")
|
|
if isinstance(data, bytes):
|
|
data = data.decode("utf-8", errors="replace")
|
|
channel = message.get("channel")
|
|
if isinstance(channel, bytes):
|
|
channel = channel.decode("utf-8", errors="replace")
|
|
payload = {
|
|
"ts": int(time.time()),
|
|
"channel": channel,
|
|
"data": data,
|
|
}
|
|
try:
|
|
handle.write(json.dumps(payload) + "\n")
|
|
handle.flush()
|
|
except OSError as exc:
|
|
log.error("failed to write audit log", error=str(exc))
|
|
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|