HTTP Record & Replay Cassette

The HTTP client hardcoded its urllib transport, so a flow that drives a real API could not be re-run in CI without the live server reachable. The client now exposes a build_call / urllib_transport seam, and this layer adds a VCR-style cassette: replay returns a recorded response for a matching request (pure, no network — the CI-valuable half), while recording is a thin pass-through over a live transport.

Pure standard library (json); imports no PySide6.

Headless API

from je_auto_control import Cassette, CassetteMissError
from je_auto_control.utils.http_client import build_call, urllib_transport

# Record once (against the live server), then save:
cassette = Cassette()
transport = cassette.recording_transport(urllib_transport)
transport(build_call("https://api.example.com/users/1", "GET"))
cassette.save("users.cassette.json")

# Replay forever — deterministic, offline:
cassette = Cassette.load("users.cassette.json")
response = cassette.replay(build_call("https://api.example.com/users/1", "GET"))
assert response["status"] == 200

build_call turns request parameters into a plain dict (url, method, headers, body, timeout) without touching the network; urllib_transport performs it. Cassette.record stores one request/response pair; replay returns the recorded response for a request matching match_on (("method", "url") by default, optionally "body") and raises CassetteMissError when nothing matches. replay_transport / recording_transport return drop-in transports so existing call sites swap live traffic for the cassette unchanged.

Executor command

AC_http_replay takes a cassette (interactions list or {interactions}, JSON string accepted), a url and optional method, and returns the recorded {response} with no network access. It is exposed as the MCP tool ac_http_replay and as a Script Builder command under Data.