- puga/: cached FIO + PRUNplanner clients, market view with order-book walk, econ formulas ported from PRUNplanner (tested against its suite and live FIO), saturation model v1 (reviewed by Opus) - tools/: scan (depth-aware), price, book, chain, state sync, plan_push (dry run default, [PuGa]-prefixed plans only), legacy prun_scan/prun_cxarb - docs/: mechanics (PRUNplanner is source of truth), roadmap, decisions, saturation design, archived handoff - secrets stay in .env (gitignored); ref/ holds PRUNplanner source (ignored) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
18 lines
757 B
Python
18 lines
757 B
Python
"""Tiny disk cache for JSON over HTTP, keyed by URL, expiry by TTL seconds."""
|
|
import hashlib, json, time, urllib.request
|
|
from .config import CACHE_DIR
|
|
|
|
UA = "PuGa/0.1 (personal advisory toolkit)"
|
|
|
|
|
|
def get_json(url: str, ttl: int, headers: dict[str, str] | None = None, refresh: bool = False):
|
|
CACHE_DIR.mkdir(parents=True, exist_ok=True)
|
|
f = CACHE_DIR / (hashlib.sha1(url.encode()).hexdigest()[:16] + ".json")
|
|
if not refresh and f.exists() and time.time() - f.stat().st_mtime < ttl:
|
|
return json.loads(f.read_text())
|
|
req = urllib.request.Request(url, headers={"User-Agent": UA, **(headers or {})})
|
|
with urllib.request.urlopen(req, timeout=60) as r:
|
|
data = json.load(r)
|
|
f.write_text(json.dumps(data))
|
|
return data
|