"""Build a standalone rein-aharness runtime for the fixed bwrap mount path. Only package installation occurs here. No credentials or model calls are used. Run with the sand-boxer Python environment; uv must be available on PATH. """ from __future__ import annotations import argparse import json import subprocess from pathlib import Path from sandboxer.extensions.runtime import RUNTIME_MOUNT, runtime_digest def checked(command: list[str]) -> str: result = subprocess.run(command, capture_output=True, text=True, timeout=300) if result.returncode: raise RuntimeError(f"runtime build command failed: {Path(command[0]).name}") return result.stdout.strip() def build(output: Path, rein_source: Path, llm_source: Path) -> dict: revisions = {} for name, source in (("rein-aharness", rein_source), ("llm-connect", llm_source)): if checked(["git", "-C", str(source), "status", "--porcelain"]): raise ValueError(f"{name} source must be committed before building") revisions[name] = checked(["git", "-C", str(source), "rev-parse", "HEAD"]) output.mkdir(parents=True, exist_ok=False) checked(["/usr/bin/python3", "-m", "venv", "--copies", "--without-pip", str(output)]) checked(["uv", "pip", "install", "--python", str(output / "bin/python3"), str(rein_source), str(llm_source)]) # Console entrypoints must reference the in-sandbox mount, not the build host. for path in (output / "bin").iterdir(): if not path.is_file() or path.is_symlink(): continue with path.open("rb") as stream: first_line = stream.readline(4096) if first_line.startswith(f"#!{output}/bin/python".encode()): body = path.read_bytes().partition(b"\n")[2] path.write_bytes(f"#!{RUNTIME_MOUNT}/bin/python3\n".encode() + body) metadata = json.loads(checked([ str(output / "bin/python3"), "-c", "import importlib.metadata,json,platform; " "print(json.dumps({'python':platform.python_version(),'packages':" "{d.metadata['Name']:d.version for d in importlib.metadata.distributions()}}))", ])) metadata["source_revisions"] = revisions (output / "build-info.json").write_text(json.dumps(metadata, indent=2) + "\n") return {"runtime": {"path": str(output), "sha256": runtime_digest(output)}, "build": metadata} def main() -> int: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--output", type=Path, required=True) parser.add_argument("--rein-source", type=Path, required=True) parser.add_argument("--llm-source", type=Path, required=True) args = parser.parse_args() result = build(args.output.resolve(), args.rein_source.resolve(), args.llm_source.resolve()) print(json.dumps(result, indent=2)) return 0 if __name__ == "__main__": raise SystemExit(main())