diff --git a/harnesses/opencode/provision.py b/harnesses/opencode/provision.py index 14ba8b0727..c7d290a2aa 100755 --- a/harnesses/opencode/provision.py +++ b/harnesses/opencode/provision.py @@ -286,8 +286,55 @@ def provision(ctx: sh.ProvisionContext) -> None: resolved_model = os.environ.get("SCION_MODEL", "").strip() _write_model_config(resolved_model) + _prefetch_models_catalog(ctx) + ctx.info(f"method={resolved.method}" + (f" model={resolved_model}" if resolved_model else "")) +# --------------------------------------------------------------------------- +# Models catalog pre-fetch +# --------------------------------------------------------------------------- + + +def _prefetch_models_catalog(ctx: sh.ProvisionContext) -> None: + """Pre-fetch latest models.dev catalog so opencode has fresh model data. + + opencode bakes a models.dev snapshot at build time; when new models are + released after the image is built, its validation rejects them. Writing a + fresh catalog to the cache location before opencode starts avoids this. + + This is best-effort — a fetch failure is logged but never fails provision. + """ + import urllib.request + + cache_dir = os.path.join(ctx.home, ".cache", "opencode") + os.makedirs(cache_dir, exist_ok=True) + cache_path = os.path.join(cache_dir, "models.json") + + try: + req = urllib.request.Request( + "https://models.opencode.ai/api.json", + headers={"User-Agent": "scion-opencode-provision/1.0"}, + ) + with urllib.request.urlopen(req, timeout=10) as resp: + data = resp.read(10 * 1024 * 1024) # 10MB cap + # Sanity-check: a real catalog is several KB of valid JSON. + if len(data) > 100: + try: + json.loads(data) + except ValueError: + ctx.info("models catalog response is not valid JSON, skipping") + return + tmp_path = cache_path + ".tmp" + with open(tmp_path, "wb") as f: + f.write(data) + os.replace(tmp_path, cache_path) + ctx.info(f"pre-fetched models catalog ({len(data)} bytes)") + else: + ctx.info("models catalog response too small, skipping") + except Exception as e: + ctx.info(f"models catalog pre-fetch failed (non-fatal): {e}") + + if __name__ == "__main__": sh.run("opencode", provision)