ouroboros/scripts/fetch_claudexor_runtime.py
Ouroboros 213c70d4de Managed Claudexor runtime delivery: exact pin, single Connect, staged updates
Ouroboros now delivers the Claudexor engine it integrates (PR #101 shipped
the integration without the runtime; a user upgrading in-app hit a dead
`claudexord_not_installed` Connect). This lands the owner-locked design:

- one exact pin (`ouroboros/claudexor_runtime_pin.json`, now bound to the
  public Claudexor 3.3.7 bytes: build a4b004d7, sha256 fe07b839…, official
  Node 24.16.0 for all five platforms) drives seed, download, verify and
  next-spawn selection; a filled pin never degrades to a PATH binary;
- hybrid delivery: the release archive ships as an offline seed in new
  DMG/tar/zip bundles and downloads foreground-only for upgraded old
  installs, strictly inside an explicit user action (Connect, Repair or a
  delegated/review start) with visible progress;
- one morphing Connect button (Install/Update/Fix & connect); updates stage
  side-by-side and activate at the next natural daemon start or Ouroboros
  restart, never hot-swapping a live daemon; a repair never replaces the
  serving target of a live matching daemon;
- 3-OS CI gate and release-artifact smokes now exercise the real managed
  chain (install → exact probe → owned daemon → delegated run →
  identity-bound graceful stop) instead of `npm install -g claudexor@next`;
- Windows fail-fast helper for critical PowerShell 5.1 steps, utf-8-pinned
  subprocess decoding, handshake reads the frozen `engine.sha` contract.

Review: triad (fable, sol scope, gemini) + adjudicated batch + confirmation
+ pin confirmation, all SAFE; suites on this base: Python 7775/1 skipped,
web 138/138, delivery tests 17/17, managed fixture smoke end-to-end.

Co-authored-by: Claudexor <noreply@claudexor.dev>
Co-authored-by: Ouroboros <311266734+ouroboros-agent@users.noreply.github.com>
2026-08-06 15:15:09 +03:00

49 lines
1.4 KiB
Python

#!/usr/bin/env python3
"""Fetch or verify the exact Claudexor archive selected by Ouroboros."""
from __future__ import annotations
import argparse
import pathlib
import sys
REPO_ROOT = pathlib.Path(__file__).resolve().parent.parent
if str(REPO_ROOT) not in sys.path:
sys.path.insert(0, str(REPO_ROOT))
from ouroboros.claudexor_runtime import ( # noqa: E402
ClaudexorRuntimeError,
fetch_runtime_archive,
load_runtime_pin,
verify_runtime_archive,
)
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--output-dir", required=True, type=pathlib.Path)
parser.add_argument("--verify-only", action="store_true")
args = parser.parse_args()
try:
pin = load_runtime_pin()
if pin is None:
raise ClaudexorRuntimeError(
"runtime_pin_unpublished",
"the Claudexor runtime release has not been pinned yet",
)
target = args.output_dir / pin.archive_name
if args.verify_only:
verify_runtime_archive(target, pin)
else:
fetch_runtime_archive(pin, target)
except ClaudexorRuntimeError as exc:
print(f"{exc.code}: {exc}", file=sys.stderr)
return 1
print(f"Claudexor {pin.version} runtime verified: {target}")
return 0
if __name__ == "__main__":
raise SystemExit(main())