From 384ac34d103612ac6dad65cf1d67c671a088efa8 Mon Sep 17 00:00:00 2001 From: Tejas Chopra Date: Fri, 13 Mar 2026 13:40:30 -0700 Subject: [PATCH] fix(wrap): route through proxy, prevent pipe deadlock, bump to 0.4.2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix ANTHROPIC_API_URL → ANTHROPIC_BASE_URL (SDK uses BASE_URL) - Write proxy logs to ~/.headroom/logs/proxy.log instead of PIPE (macOS 64KB pipe buffer fills up, blocking the proxy process) - Add --auto-patch to rtk init to avoid interactive prompt timeout - Bump version to 0.4.2 Co-Authored-By: Claude Opus 4.6 (1M context) --- headroom/__init__.py | 2 +- headroom/cli/wrap.py | 32 +++++++++++++++++++++++++++----- pyproject.toml | 2 +- 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/headroom/__init__.py b/headroom/__init__.py index 372142d61..6ff530353 100644 --- a/headroom/__init__.py +++ b/headroom/__init__.py @@ -142,7 +142,7 @@ from .transforms import ( TransformPipeline, ) -__version__ = "0.4.1" +__version__ = "0.4.2" __all__ = [ # Main client diff --git a/headroom/cli/wrap.py b/headroom/cli/wrap.py index 4dcf9938e..744d3ac10 100644 --- a/headroom/cli/wrap.py +++ b/headroom/cli/wrap.py @@ -37,27 +37,49 @@ def _check_proxy(port: int) -> bool: return False +def _get_log_path() -> Path: + """Get path for proxy log file.""" + log_dir = Path.home() / ".headroom" / "logs" + log_dir.mkdir(parents=True, exist_ok=True) + return log_dir / "proxy.log" + + def _start_proxy(port: int) -> subprocess.Popen: - """Start Headroom proxy as a background subprocess.""" + """Start Headroom proxy as a background subprocess. + + Logs are written to ~/.headroom/logs/proxy.log to avoid pipe buffer + deadlocks (macOS pipe buffer is ~64KB — a busy proxy fills it quickly, + blocking the process). + """ cmd = [sys.executable, "-m", "headroom.cli", "proxy", "--port", str(port)] + log_path = _get_log_path() + log_file = open(log_path, "a") # noqa: SIM115 + proc = subprocess.Popen( cmd, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, + stdout=log_file, + stderr=log_file, ) # Wait for proxy to be ready (up to 15 seconds) for _i in range(15): time.sleep(1) if _check_proxy(port): + click.echo(f" Logs: {log_path}") return proc # Check if process died if proc.poll() is not None: - stderr = proc.stderr.read().decode() if proc.stderr else "" - raise RuntimeError(f"Proxy exited with code {proc.returncode}: {stderr[:500]}") + log_file.close() + # Read last few lines of log for error context + try: + tail = log_path.read_text()[-500:] + except Exception: + tail = "(no log output)" + raise RuntimeError(f"Proxy exited with code {proc.returncode}: {tail}") proc.kill() + log_file.close() raise RuntimeError(f"Proxy failed to start on port {port} within 15 seconds") diff --git a/pyproject.toml b/pyproject.toml index c7a1342fb..0b9023db5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "headroom-ai" -version = "0.4.1" +version = "0.4.2" description = "The Context Optimization Layer for LLM Applications - Cut costs by 50-90%" readme = "README.md" license = "Apache-2.0"