Ctrl K
ComunidadGuías…

hooklog.py

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
"""
A one-line note each time an agent runs Squidbrake's hook, so `squidbrake doctor` can tell "connected, but the agent
never called it" from "connected and working". Only which agent, which hook event and when: never the command, the
file, the input or the key. Kept in ~/.squidbrake/hooks.log (SQUIDBRAKE_HOME moves it), about 250 KB at most.
"""
from __future__ import annotations

import json
import os
from datetime import datetime, timezone
from pathlib import Path

MAX_BYTES = 250_000


def path() -> Path:
    if os.getenv("SQUIDBRAKE_HOOKLOG"):       # tests point it elsewhere
        return Path(os.environ["SQUIDBRAKE_HOOKLOG"])
    return Path(os.getenv("SQUIDBRAKE_HOME") or Path.home() / ".squidbrake") / "hooks.log"


def record(agent: str, event: str) -> None:
    """Never raises: a hook must not fail because of its own bookkeeping."""
    try:
        p = path()
        p.parent.mkdir(parents=True, exist_ok=True)
        if p.exists() and p.stat().st_size > MAX_BYTES:
            p.replace(p.with_name("hooks.log.1"))
        line = {"t": datetime.now(timezone.utc).isoformat(timespec="milliseconds"), "agent": agent, "event": event[:60]}
        if os.getenv("SQUIDBRAKE_DOCTOR"):
            line["doctor"] = True
        with p.open("a", encoding="utf-8") as f:
            f.write(json.dumps(line) + "\n")
    except Exception:
        pass


def last_call(agent: str) -> datetime | None:
    """When the agent itself (not a doctor test) last ran the hook."""
    found = None
    for p in (path().with_name("hooks.log.1"), path()):
        try:
            lines = p.read_text(encoding="utf-8", errors="replace").splitlines()
        except OSError:
            continue
        for raw in lines:
            try:
                d = json.loads(raw)
            except ValueError:
                continue
            if d.get("agent") == agent and not d.get("doctor"):
                found = datetime.fromisoformat(d["t"])
    return found


def daily(url: str) -> None:
    """Once a day, from whichever agent's hook runs first: connect agents installed since setup (connect.py auto). Only
    for a hosted dashboard; a gateway on this computer does it itself. Starts it in the background: never waits,
    never raises, and never from the tests (SQUIDBRAKE_HOOKLOG) or a doctor check."""
    host = url.split("//", 1)[-1].split("/", 1)[0].split(":", 1)[0].lower()
    if os.getenv("SQUIDBRAKE_HOOKLOG") or os.getenv("SQUIDBRAKE_DOCTOR") or host in ("localhost", "127.0.0.1", "::1", "[::1]"):
        return
    try:
        import subprocess
        import sys
        import time
        stamp = path().with_name("auto-connect.stamp")
        if stamp.exists() and time.time() - stamp.stat().st_mtime < 86400:
            return
        stamp.parent.mkdir(parents=True, exist_ok=True)
        stamp.touch()
        here = Path(__file__).resolve().parent
        run = dict(cwd=str(here), stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, close_fds=True)
        cmd = [sys.executable, str(here / "connect.py"), "auto"]
        if sys.platform != "win32":
            subprocess.Popen(cmd, start_new_session=True, **run)
            return
        quiet = getattr(subprocess, "CREATE_NO_WINDOW", 0)
        try:   # out of the agent's job, which may end every process the hook started once the hook returns
            subprocess.Popen(cmd, creationflags=quiet | getattr(subprocess, "CREATE_BREAKAWAY_FROM_JOB", 0), **run)
        except OSError:
            subprocess.Popen(cmd, creationflags=quiet, **run)
    except Exception:
        pass