Initial resurrection kit — 81 scripts, 62 references, configs, systemd units, crons, Docker compose files, Caddy config, master README
This commit is contained in:
Executable
+85
@@ -0,0 +1,85 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Track Firecrawl API credit usage for portal dashboard."""
|
||||
import json, os, time
|
||||
from datetime import datetime, timezone
|
||||
BASE = os.path.expanduser("~/.hermes/scripts")
|
||||
|
||||
LOG = os.path.join(BASE, "firecrawl-usage.json")
|
||||
LIMIT = 1000 # Current tier
|
||||
|
||||
def _defaults():
|
||||
return {"total_used": 0, "calls": [], "monthly": {}, "last_reset": str(datetime.now(timezone.utc))}
|
||||
|
||||
def load():
|
||||
"""Load usage data, merging in any missing top-level keys.
|
||||
|
||||
Older state files were written without the `monthly` / `calls` keys,
|
||||
which caused KeyError crashes in summary(). Merging defaults makes the
|
||||
loader forward/backward compatible with partial state files.
|
||||
"""
|
||||
data = _defaults()
|
||||
if os.path.exists(LOG):
|
||||
try:
|
||||
with open(LOG) as f:
|
||||
loaded = json.load(f)
|
||||
if isinstance(loaded, dict):
|
||||
data.update(loaded)
|
||||
except (json.JSONDecodeError, OSError):
|
||||
pass # Corrupt/unreadable state — fall back to defaults
|
||||
# Guarantee required keys/types even if the file had them as null/wrong type
|
||||
if not isinstance(data.get("monthly"), dict):
|
||||
data["monthly"] = {}
|
||||
if not isinstance(data.get("calls"), list):
|
||||
data["calls"] = []
|
||||
if not isinstance(data.get("total_used"), (int, float)):
|
||||
data["total_used"] = 0
|
||||
return data
|
||||
|
||||
def save(data):
|
||||
with open(LOG, "w") as f:
|
||||
json.dump(data, f, indent=2)
|
||||
|
||||
def record(credits_used, endpoint, url=""):
|
||||
data = load()
|
||||
now = datetime.now(timezone.utc)
|
||||
month_key = now.strftime("%Y-%m")
|
||||
|
||||
# Track total
|
||||
data["total_used"] += credits_used
|
||||
|
||||
# Track monthly
|
||||
if month_key not in data["monthly"]:
|
||||
data["monthly"][month_key] = 0
|
||||
data["monthly"][month_key] += credits_used
|
||||
|
||||
# Log individual call
|
||||
data["calls"].append({
|
||||
"ts": now.isoformat(),
|
||||
"endpoint": endpoint,
|
||||
"url": url[:100],
|
||||
"credits": credits_used
|
||||
})
|
||||
|
||||
# Trim call log to last 1000
|
||||
data["calls"] = data["calls"][-1000:]
|
||||
save(data)
|
||||
|
||||
def summary():
|
||||
data = load()
|
||||
month_key = datetime.now(timezone.utc).strftime("%Y-%m")
|
||||
month_used = data["monthly"].get(month_key, 0)
|
||||
return {
|
||||
"total_used": data["total_used"],
|
||||
"month_used": month_used,
|
||||
"month_limit": LIMIT,
|
||||
"month_remaining": max(0, LIMIT - month_used),
|
||||
"month_pct": round(month_used / LIMIT * 100, 1),
|
||||
"recent_calls": data["calls"][-10:]
|
||||
}
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
if len(sys.argv) > 1 and sys.argv[1] == "summary":
|
||||
print(json.dumps(summary(), indent=2))
|
||||
else:
|
||||
print(f"Credits: {summary()['month_used']}/{LIMIT} used this month")
|
||||
Reference in New Issue
Block a user