#!/usr/bin/env python3 """Build a portable seed from public source responses and metrics, without secrets.""" from datetime import datetime, timezone import gzip import hashlib import io import json from pathlib import Path import tarfile import subprocess import sys import tempfile ROOT = Path(__file__).resolve().parents[1] def main(): source = ROOT / "space/vendor/ts_bench/data/sample_week" files = [p for name in ("normalized", "raw") for p in (source / name).rglob("*") if p.is_file()] if (source / "summary.json").exists(): files.append(source / "summary.json") for p in (ROOT / "space/results").rglob("*"): if not p.is_file(): continue rel = p.relative_to(ROOT / "space/results") if any(part in rel.parts for part in ("prequential", "saved_evals", "forecasts", "canonical_all_history")): continue if p.suffix not in (".json", ".csv", ".tex", ".jsonl") or p.name == "eval_state.json": continue if p.suffix == ".jsonl" and rel.as_posix() not in ("evaluation_metrics_canonical.jsonl", "eval_history.jsonl"): continue files.append(p) # Check literal configured credentials before any file enters the archive. from dotenv import dotenv_values secrets = [v.encode() for k, v in dotenv_values(ROOT / ".env").items() if v and len(v) >= 12 and any(word in k for word in ("TOKEN", "KEY", "SECRET", "PASSWORD"))] members = [] for p in sorted(files): content = p.read_bytes() if any(secret in content for secret in secrets): raise SystemExit(f"Credential found in seed candidate: {p.relative_to(ROOT)}") if p.name == "online_status.json": status = json.loads(content) status.update(status="bootstrap", issued_forecasts=0, resolved_forecasts=0, pending_tasks=0, issued_tasks=0, push_status="not_published", bootstrap_note="Private pending forecast state is excluded; start a new live cycle.") content = (json.dumps(status, indent=2) + "\n").encode() members.append((p.relative_to(ROOT).as_posix(), content)) # Export into an empty directory: old runtime exports otherwise accumulate # tens of thousands of unreachable task files across collection cycles. with tempfile.TemporaryDirectory(prefix="livehouse-seed-inputs-") as temp: subprocess.run([sys.executable, str(ROOT / "space/vendor/ts_bench/export_model_inputs.py"), "--input", str(source / "normalized"), "--output", temp, "--limit", "20"], check=True) prefix = "space/vendor/ts_bench/data/sample_week/model_inputs_latest/" members.extend((prefix + p.relative_to(temp).as_posix(), p.read_bytes()) for p in Path(temp).rglob("*") if p.is_file()) archive = ROOT / "bootstrap/seed.tar.gz" with archive.open("wb") as raw, gzip.GzipFile(filename="", fileobj=raw, mode="wb", mtime=0) as compressed: with tarfile.open(fileobj=compressed, mode="w") as bundle: for name, content in members: info = tarfile.TarInfo(name) info.size = len(content) info.mode = 0o644 bundle.addfile(info, io.BytesIO(content)) manifest = {"snapshot_date": datetime.now(timezone.utc).date().isoformat(), "sha256": hashlib.sha256(archive.read_bytes()).hexdigest(), "bytes": archive.stat().st_size, "files": len(members), "contents": "Current public raw responses, normalized observations, latest inputs, historical canonical metrics, public tables and forecast visualization snapshots. No credentials or private task/inference archives.", "historical_raw_available": False, "current_raw_included": True} (ROOT / "bootstrap/manifest.json").write_text(json.dumps(manifest, indent=2) + "\n") print(json.dumps(manifest, indent=2)) if __name__ == "__main__": main()