Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a07ff8-19d0-7820-b4d0-1353833cb7fc
171 lines
8.7 KiB
Python
171 lines
8.7 KiB
Python
"""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 hashlib
|
|
import json
|
|
import os
|
|
import re
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
from sandboxer.extensions.runtime import RUNTIME_MOUNT, runtime_digest
|
|
|
|
|
|
def checked(command: list[str], *, env: dict[str, str] | None = None) -> str:
|
|
result = subprocess.run(command, capture_output=True, text=True, timeout=300, env=env)
|
|
if result.returncode:
|
|
raise RuntimeError(f"runtime build command failed: {Path(command[0]).name}")
|
|
return result.stdout.strip()
|
|
|
|
|
|
def install_claude(output: Path, source: Path, sha256: str, version: str) -> dict:
|
|
"""Copy only an explicitly pinned native executable, never an interactive HOME."""
|
|
if not re.fullmatch(r"[0-9a-f]{64}", sha256) or not version.strip():
|
|
raise ValueError("Claude requires an exact SHA-256 and expected version")
|
|
if source.is_symlink() or not source.is_file():
|
|
raise ValueError("Claude source must be a regular file, not a symlink")
|
|
content = source.read_bytes()
|
|
if hashlib.sha256(content).hexdigest() != sha256:
|
|
raise ValueError("Claude executable digest mismatch")
|
|
if not content.startswith(b"\x7fELF"):
|
|
raise ValueError("Claude executable must be native ELF, not a host wrapper")
|
|
destination = output / "bin/claude"
|
|
with destination.open("xb") as stream:
|
|
stream.write(content)
|
|
destination.chmod(0o755)
|
|
return {"path": f"{RUNTIME_MOUNT}/bin/claude", "sha256": sha256,
|
|
"expected_version": version, "source": str(source)}
|
|
|
|
|
|
def verify_source_files(site: Path, mappings: list[tuple[Path, str, str]]) -> dict:
|
|
"""Refuse stale/missing/extra package contents even when versions match."""
|
|
expected = {}
|
|
for source, prefix, destination in mappings:
|
|
tracked = checked(["git", "-C", str(source), "ls-files", prefix]).splitlines()
|
|
if not tracked:
|
|
raise ValueError("owner package source mapping is empty")
|
|
for name in tracked:
|
|
relative = Path(destination) / Path(name).relative_to(prefix)
|
|
wanted = hashlib.sha256((source / name).read_bytes()).hexdigest()
|
|
installed = site / relative
|
|
if (not installed.is_file() or installed.is_symlink()
|
|
or hashlib.sha256(installed.read_bytes()).hexdigest() != wanted):
|
|
raise ValueError(f"owner package content mismatch: {relative}")
|
|
expected[relative.as_posix()] = wanted
|
|
actual = set()
|
|
for package in {Path(name).parts[0] for name in expected}:
|
|
for path in (site / package).rglob("*"):
|
|
if path.is_file() and "__pycache__" not in path.parts:
|
|
actual.add(path.relative_to(site).as_posix())
|
|
if actual != set(expected):
|
|
raise ValueError("owner package has unexpected installed files")
|
|
return {"files_verified": len(expected), "manifest_sha256": hashlib.sha256(
|
|
json.dumps(expected, sort_keys=True, separators=(",", ":")).encode()
|
|
).hexdigest()}
|
|
|
|
|
|
def build(output: Path, rein_source: Path, llm_source: Path,
|
|
claude_binary: Path | None = None, claude_sha256: str | None = None,
|
|
claude_version: str | None = None, *, owner_runtime: bool = False) -> dict:
|
|
supplied = (claude_binary, claude_sha256, claude_version)
|
|
if any(x is not None for x in supplied) and not all(x is not None for x in supplied):
|
|
raise ValueError("Claude binary, SHA-256 and expected version must be supplied together")
|
|
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"])
|
|
lock_report = None
|
|
if owner_runtime:
|
|
lock_report = json.loads(checked([
|
|
"/usr/bin/python3", str(rein_source / "scripts/verify_runtime_lock.py")
|
|
]))
|
|
if not lock_report.get("ok"):
|
|
raise ValueError("owner runtime requires the verified sibling lock")
|
|
if next(x["commit"] for x in lock_report["dependencies"]
|
|
if x["distribution"] == "llm-connect") != revisions["llm-connect"]:
|
|
raise ValueError("owner runtime llm source does not match sibling lock")
|
|
revisions.update({x["distribution"]: x["commit"] for x in lock_report["dependencies"]})
|
|
output.mkdir(parents=True, exist_ok=False)
|
|
checked(["/usr/bin/python3", "-m", "venv", "--copies", "--without-pip", str(output)])
|
|
if owner_runtime:
|
|
refresh = [arg for name in revisions for arg in ("--refresh-package", name)]
|
|
checked(["uv", "sync", "--project", str(rein_source), "--frozen", "--no-editable",
|
|
"--no-dev", "--extra", "glas", "--extra", "llm", "--python", "/usr/bin/python3",
|
|
*refresh],
|
|
env={**os.environ, "UV_PROJECT_ENVIRONMENT": str(output)})
|
|
else:
|
|
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()}}))",
|
|
]))
|
|
if claude_binary is not None:
|
|
metadata["claude"] = install_claude(output, claude_binary, claude_sha256, claude_version)
|
|
metadata["source_revisions"] = revisions
|
|
if owner_runtime:
|
|
lock = json.loads((rein_source / "deploy/runtime-contract-lock.json").read_text())
|
|
sources = {x["distribution"]: (rein_source / x["source"]).resolve()
|
|
for x in lock["dependencies"]}
|
|
site = Path(checked([str(output / "bin/python3"), "-I", "-B", "-c",
|
|
"import sysconfig; print(sysconfig.get_path('purelib'))"]))
|
|
if not site.resolve().is_relative_to(output.resolve()):
|
|
raise ValueError("owner site-packages is outside artifact")
|
|
metadata["source_contents"] = verify_source_files(site, [
|
|
(rein_source, "rein_aharness", "rein_aharness"),
|
|
(sources["llm-connect"], "llm_connect", "llm_connect"),
|
|
(sources["glas-harness"], "src/glas_harness", "glas_harness"),
|
|
(sources["glas-harness"], "profiles", "glas_harness/data/profiles"),
|
|
(sources["glas-harness"], "registry/reins", "glas_harness/data/reins"),
|
|
(sources["sandboxer"], "src/sandboxer", "sandboxer"),
|
|
(sources["sandboxer"], "profiles", "sandboxer/data/profiles"),
|
|
(sources["sandboxer"], "extensions", "sandboxer/data/extensions"),
|
|
])
|
|
metadata["owner_runtime"] = True
|
|
metadata["runtime_contract"] = lock_report
|
|
metadata["uv_lock_sha256"] = hashlib.sha256(
|
|
(rein_source / "uv.lock").read_bytes()
|
|
).hexdigest()
|
|
(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)
|
|
parser.add_argument("--claude-binary", type=Path)
|
|
parser.add_argument("--claude-sha256")
|
|
parser.add_argument("--claude-version")
|
|
parser.add_argument("--owner-runtime", action="store_true",
|
|
help="Include the pinned owner packages from rein uv.lock")
|
|
args = parser.parse_args()
|
|
result = build(args.output.resolve(), args.rein_source.resolve(), args.llm_source.resolve(),
|
|
args.claude_binary, args.claude_sha256, args.claude_version,
|
|
owner_runtime=args.owner_runtime)
|
|
print(json.dumps(result, indent=2))
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|