Files
time/quc/deploy.py
T
teahubxyz f479c5f016
ci / test (push) Successful in 52s
quc / deploy (push) Failing after 3m34s
sync / sync (push) Successful in 10s
time/worker-run cd .github/time && python3 - <<'E' p='time.py'; s=open(p).read() s=s.replace(''' try: ret
quc v0: the cage, the chair, the safeword, the magic words, the replay (#241)
2026-10-22 18:26:41 +00:00

71 lines
3.1 KiB
Python

"""Deploy quc to quc.<TIME_VENTURE_DOMAIN> (default yolopreneur.lol) as the Worker `time-quc`, then wait for /api/health.
CLOUDFLARE_API_TOKEN=... CLOUDFLARE_ACCOUNT_ID=... VENICE_API_KEY=... python3 quc/deploy.py
Reuses .github/time/cloudflare.py. Idempotent: the KV namespace `time-quc` is created once and reused, the script is replaced
in place, the custom domain re-attached. VENICE_API_KEY is bound as a Worker secret (without it the agent answers offline).
"""
from __future__ import annotations
import hashlib
import json
import os
import sys
import time
import urllib.request
from pathlib import Path
HERE = Path(__file__).resolve().parent
sys.path.insert(0, str(HERE.parent / ".github" / "time"))
import cloudflare as cf # noqa: E402
NAME = "time-quc"
MODULES = ["worker.js", "page.js", "runs.js", "engine.js", "agent.js", "diff.js", "validate.js"]
def healthy(url: str, tries: int = 18) -> bool:
for _ in range(tries):
try:
with urllib.request.urlopen(url + "api/health", timeout=10) as r:
if json.load(r).get("ok") is True:
return True
except Exception: # noqa: BLE001 not up yet (DNS, cert, cold start): keep polling
pass
time.sleep(10)
return False
def kv_namespace(title: str) -> str:
for ns in cf.cf("GET", f"{cf.account()}/storage/kv/namespaces?per_page=100") or []:
if ns.get("title") == title:
return ns["id"]
return cf.cf("POST", f"{cf.account()}/storage/kv/namespaces", {"title": title})["id"]
def deploy() -> dict:
if not cf.enabled():
sys.exit("CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID are needed")
os.environ.setdefault("TIME_VENTURE_DOMAIN", "yolopreneur.lol")
code = {m: (HERE / "src" / m).read_bytes() for m in MODULES}
rev = hashlib.sha256(b"".join(code[m] for m in MODULES)).hexdigest()[:12]
bindings = [{"type": "kv_namespace", "name": "KV", "namespace_id": kv_namespace(NAME)},
{"type": "plain_text", "name": "REV", "text": rev}]
for k in ("CODER_MODEL", "PERSONA_MODEL", "RUNS_PER_DAY", "RUNS_PER_DAY_ALL"):
if os.environ.get(k):
bindings.append({"type": "plain_text", "name": k, "text": os.environ[k]})
if os.environ.get("VENICE_API_KEY"):
bindings.append({"type": "secret_text", "name": "VENICE_API_KEY", "text": os.environ["VENICE_API_KEY"]})
else:
print("warning: no VENICE_API_KEY, so the agent gives offline answers", file=sys.stderr)
meta = {"main_module": "worker.js", "compatibility_date": cf.COMPAT, "bindings": bindings}
data, ctype = cf.multipart([("metadata", "application/json", json.dumps(meta).encode())]
+ [(m, "application/javascript+module", code[m]) for m in MODULES])
cf.cf("PUT", f"{cf.account()}/workers/scripts/{NAME}", data=data, ctype=ctype)
return {"url": cf.attach(NAME, cf.host("quc")), "worker": NAME, "rev": rev}
if __name__ == "__main__":
out = deploy()
print(json.dumps(out, indent=1))
sys.exit(0 if healthy(out["url"]) else "deployed, but /api/health never answered ok")