61 lines
4.1 KiB
Python
Executable file
61 lines
4.1 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
"""Builds ~/Desktop/fleet/fleet.json (the fleet page's single source) from the registry (boxes.json), the ledger, the
|
|
result rows (results.json: {card_key: {...}}), the night rows (night.json: [..]), the phases (phases.json) and the log
|
|
(log.jsonl), then publishes it with the master checkout's tools/fleet/publish-fleet.sh (at most once a minute).
|
|
|
|
page.py log "<text>" append a log line (and rebuild, no publish)
|
|
page.py phase p1 state=running done=3 note="..."
|
|
page.py build rebuild only
|
|
page.py publish rebuild and publish (skipped when the last publish was under 60 s ago unless --force)
|
|
"""
|
|
import json, os, sys, time, datetime, subprocess
|
|
ROOT = os.path.expanduser("~/Desktop/fleet"); OUT = os.path.join(ROOT, "fleet.json")
|
|
PUBLISH = "/Users/joshm/Projects/igneum/tools/fleet/publish-fleet.sh"
|
|
def now(): return datetime.datetime.now(datetime.timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
def jl(name, default):
|
|
p = os.path.join(ROOT, name)
|
|
if not os.path.exists(p): return default
|
|
if name.endswith(".jsonl"): return [json.loads(l) for l in open(p) if l.strip()]
|
|
return json.load(open(p))
|
|
def hours(a, b=None):
|
|
t0 = datetime.datetime.strptime(a, "%Y-%m-%dT%H:%M:%SZ")
|
|
t1 = datetime.datetime.strptime(b, "%Y-%m-%dT%H:%M:%SZ") if b else datetime.datetime.utcnow()
|
|
return max((t1 - t0).total_seconds(), 0) / 3600
|
|
def build():
|
|
reg = jl("boxes.json", {}); res = jl("results.json", {}); night = jl("night.json", []); log = jl("log.jsonl", [])
|
|
phases = jl("phases.json", [
|
|
{"order": 1, "name": "Memory matrix on real cards", "state": "planned", "planned": 11, "done": 0, "note": ""},
|
|
{"order": 2, "name": "Prover fleet on the devnet", "state": "planned", "planned": 25, "done": 0, "note": ""},
|
|
{"order": 3, "name": "The 8x rigs", "state": "planned", "planned": 2, "done": 0, "note": ""},
|
|
{"order": 4, "name": "AMD mining", "state": "planned", "planned": 3, "done": 0, "note": ""}])
|
|
boxes = []; vast = 0.0; runpod = 0.0
|
|
for iid, b in reg.items():
|
|
h = b.get("hours") if b.get("state") == "destroyed" else hours(b["rented_at"])
|
|
cost = round(h * b["dph"], 3)
|
|
if b.get("provider", "vast") == "vast": vast += cost
|
|
else: runpod += cost
|
|
boxes.append({"id": iid, "label": b["label"], "card": b["card"], "vram_gb": round((b.get("vram_mb") or 0) / 1024), "provider": b.get("provider", "vast"),
|
|
"rate_usd_h": b["dph"], "phase": b.get("phase", "1"), "state": "done" if b.get("state") == "destroyed" else b.get("state", "renting"),
|
|
"started_at": b["rented_at"], "ended_at": b.get("destroyed_at"), "hours": round(h, 2), "cost_usd": cost, "doing": b.get("doing", "")})
|
|
page = {"spend": {"vast_usd": round(vast, 2), "runpod_usd": round(runpod, 2), "cap_vast": 900, "cap_runpod": 450, "updated_at": now()},
|
|
"phases": phases, "boxes": boxes, "results": list(res.values()), "night": night, "log": log[-200:]}
|
|
json.dump(page, open(OUT, "w"), indent=1); return page
|
|
def publish(force=False):
|
|
stamp = os.path.join(ROOT, ".last-publish")
|
|
if not force and os.path.exists(stamp) and time.time() - os.path.getmtime(stamp) < 60: print("published under a minute ago; skipped"); return
|
|
build(); open(stamp, "w").write(now())
|
|
r = subprocess.run([PUBLISH, OUT], capture_output=True, text=True, timeout=300); print((r.stdout + r.stderr).strip()[-300:])
|
|
if __name__ == "__main__":
|
|
c = sys.argv[1] if len(sys.argv) > 1 else "build"
|
|
if c == "log":
|
|
with open(os.path.join(ROOT, "log.jsonl"), "a") as f: f.write(json.dumps({"at": now(), "text": sys.argv[2]}) + "\n")
|
|
build()
|
|
elif c == "phase":
|
|
ph = jl("phases.json", None) or build()["phases"]
|
|
for p in ph:
|
|
if f"p{p['order']}" == sys.argv[2]:
|
|
for kv in sys.argv[3:]:
|
|
k, v = kv.split("=", 1); p[k] = int(v) if v.isdigit() else v
|
|
json.dump(ph, open(os.path.join(ROOT, "phases.json"), "w")); build()
|
|
elif c == "publish": publish("--force" in sys.argv)
|
|
else: build(); print(OUT)
|