From d60cf7914c5ca176b4c763ae6909376bfc2f68f9 Mon Sep 17 00:00:00 2001 From: JerrettDavis Date: Tue, 21 Apr 2026 22:44:38 -0500 Subject: [PATCH 01/16] fix: support live copilot oauth runtime Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- headroom/copilot_auth.py | 134 +++++++++++++++++++++++++ headroom/proxy/handlers/openai.py | 12 +-- tests/test_cli/test_wrap_copilot.py | 100 ++++++++++-------- tests/test_copilot_auth.py | 118 ++++++++++++++++++++-- tests/test_proxy_copilot_auth_hooks.py | 49 ++++----- 5 files changed, 329 insertions(+), 84 deletions(-) diff --git a/headroom/copilot_auth.py b/headroom/copilot_auth.py index d31f56e19..a4540bd00 100644 --- a/headroom/copilot_auth.py +++ b/headroom/copilot_auth.py @@ -3,10 +3,13 @@ from __future__ import annotations import asyncio +import ctypes import json import logging import os +import subprocess import time +from ctypes import wintypes from dataclasses import dataclass from datetime import datetime from pathlib import Path @@ -67,6 +70,11 @@ def _token_exchange_url() -> str: return os.environ.get("GITHUB_COPILOT_TOKEN_EXCHANGE_URL", DEFAULT_TOKEN_EXCHANGE_URL).strip() +def _should_exchange_oauth_token() -> bool: + raw = os.environ.get("GITHUB_COPILOT_USE_TOKEN_EXCHANGE", "").strip().lower() + return raw in {"1", "true", "yes", "on"} + + def _resolve_token_file_paths() -> list[Path]: override = os.environ.get("GITHUB_COPILOT_TOKEN_FILE", "").strip() if override: @@ -83,6 +91,104 @@ def _resolve_token_file_paths() -> list[Path]: return paths +def _read_gh_cli_oauth_token() -> str | None: + gh_bin = os.environ.get("GH_PATH", "").strip() or "gh" + command = [gh_bin, "auth", "token"] + host = _github_host() + if host and host != DEFAULT_GITHUB_HOST: + command.extend(["--hostname", host]) + + try: + result = subprocess.run( + command, + capture_output=True, + text=True, + encoding="utf-8", + errors="replace", + check=False, + ) + except OSError as exc: + logger.debug("Unable to invoke GitHub CLI for Copilot auth discovery: %s", exc) + return None + + if result.returncode != 0: + logger.debug("GitHub CLI auth token lookup failed with exit code %s", result.returncode) + return None + + token = result.stdout.strip() + return token or None + + +def _read_windows_copilot_cli_oauth_token() -> str | None: + if os.name != "nt": + return None + + class FILETIME(ctypes.Structure): + _fields_ = [ + ("dwLowDateTime", wintypes.DWORD), + ("dwHighDateTime", wintypes.DWORD), + ] + + class CREDENTIAL(ctypes.Structure): + _fields_ = [ + ("Flags", wintypes.DWORD), + ("Type", wintypes.DWORD), + ("TargetName", wintypes.LPWSTR), + ("Comment", wintypes.LPWSTR), + ("LastWritten", FILETIME), + ("CredentialBlobSize", wintypes.DWORD), + ("CredentialBlob", ctypes.POINTER(ctypes.c_ubyte)), + ("Persist", wintypes.DWORD), + ("AttributeCount", wintypes.DWORD), + ("Attributes", wintypes.LPVOID), + ("TargetAlias", wintypes.LPWSTR), + ("UserName", wintypes.LPWSTR), + ] + + cred_ptr = ctypes.POINTER(CREDENTIAL) + credentials = ctypes.POINTER(cred_ptr)() + count = wintypes.DWORD() + advapi32 = ctypes.WinDLL("Advapi32.dll") + advapi32.CredEnumerateW.argtypes = [ + wintypes.LPCWSTR, + wintypes.DWORD, + ctypes.POINTER(wintypes.DWORD), + ctypes.POINTER(ctypes.POINTER(cred_ptr)), + ] + advapi32.CredEnumerateW.restype = wintypes.BOOL + advapi32.CredFree.argtypes = [wintypes.LPVOID] + + try: + if not advapi32.CredEnumerateW(None, 0, ctypes.byref(count), ctypes.byref(credentials)): + return None + except OSError as exc: + logger.debug("Unable to enumerate Windows credentials for Copilot auth discovery: %s", exc) + return None + + host = _github_host().lower() + service_prefixes = [f"copilot-cli/{host}:"] + if "://" not in host: + service_prefixes.append(f"copilot-cli/https://{host}:") + + try: + for idx in range(count.value): + credential = credentials[idx].contents + target = (credential.TargetName or "").strip().lower() + if not any(target.startswith(prefix) for prefix in service_prefixes): + continue + if credential.CredentialBlobSize <= 0 or not credential.CredentialBlob: + continue + blob = ctypes.string_at(credential.CredentialBlob, credential.CredentialBlobSize) + token = blob.decode("utf-8", errors="replace").strip() + if token: + return token + finally: + if credentials: + advapi32.CredFree(credentials) + + return None + + def _parse_expiry(value: Any) -> float | None: if value in (None, ""): return None @@ -157,6 +263,14 @@ def read_cached_oauth_token() -> str | None: if token: return token + windows_copilot_token = _read_windows_copilot_cli_oauth_token() + if windows_copilot_token: + return windows_copilot_token + + gh_token = _read_gh_cli_oauth_token() + if gh_token: + return gh_token + host = _github_host() for path in _resolve_token_file_paths(): try: @@ -203,6 +317,16 @@ def is_copilot_api_url(url: str | None) -> bool: return "githubcopilot.com" in host +def build_copilot_upstream_url(base_url: str, path: str) -> str: + """Build an upstream URL, normalizing GitHub Copilot's non-/v1 path layout.""" + + normalized_base = base_url.rstrip("/") + normalized_path = path if path.startswith("/") else f"/{path}" + if is_copilot_api_url(normalized_base) and normalized_path.startswith("/v1/"): + normalized_path = normalized_path[3:] + return f"{normalized_base}{normalized_path}" + + class CopilotTokenProvider: """Resolve and cache short-lived Copilot API tokens.""" @@ -233,6 +357,16 @@ class CopilotTokenProvider: if not oauth_token: raise RuntimeError("No GitHub Copilot OAuth token is available.") + if not _should_exchange_oauth_token(): + direct_token = CopilotAPIToken( + token=oauth_token, + expires_at=time.time() + 3600, + api_url=os.environ.get("GITHUB_COPILOT_API_URL", DEFAULT_API_URL).strip() + or DEFAULT_API_URL, + ) + self._cached = direct_token + return direct_token + exchanged = await self._exchange_token(oauth_token) self._cached = exchanged return exchanged diff --git a/headroom/proxy/handlers/openai.py b/headroom/proxy/handlers/openai.py index e80dbc5de..7cbb0dd30 100644 --- a/headroom/proxy/handlers/openai.py +++ b/headroom/proxy/handlers/openai.py @@ -31,7 +31,7 @@ if TYPE_CHECKING: import httpx -from headroom.copilot_auth import apply_copilot_api_auth +from headroom.copilot_auth import apply_copilot_api_auth, build_copilot_upstream_url logger = logging.getLogger("headroom.proxy") @@ -558,7 +558,7 @@ class OpenAIHandlerMixin: ) # Direct OpenAI API (no backend configured) - url = f"{self.OPENAI_API_URL}/v1/chat/completions" + url = build_copilot_upstream_url(self.OPENAI_API_URL, "/v1/chat/completions") try: if stream: @@ -1075,7 +1075,7 @@ class OpenAIHandlerMixin: if is_chatgpt_auth: url = "https://chatgpt.com/backend-api/codex/responses" else: - url = f"{self.OPENAI_API_URL}/v1/responses" + url = build_copilot_upstream_url(self.OPENAI_API_URL, "/v1/responses") try: if stream: @@ -1354,7 +1354,7 @@ class OpenAIHandlerMixin: # API key auth → route to configured OpenAI API URL base = self.OPENAI_API_URL ws_base = base.replace("https://", "wss://").replace("http://", "ws://") - upstream_url = f"{ws_base}/v1/responses" + upstream_url = build_copilot_upstream_url(ws_base, "/v1/responses") # Unit 3: attach the resolved upstream URL to the session handle. if session_handle is not None: @@ -2163,7 +2163,7 @@ class OpenAIHandlerMixin: if "chatgpt-account-id" in _lower: http_url = "https://chatgpt.com/backend-api/codex/responses" else: - http_url = f"{self.OPENAI_API_URL}/v1/responses" + http_url = build_copilot_upstream_url(self.OPENAI_API_URL, "/v1/responses") # Build HTTP body from the WS response.create payload. # WS messages use {"type": "response.create", "response": {...}} wrapper. @@ -2457,7 +2457,7 @@ class OpenAIHandlerMixin: start_time = time.time() path = request.url.path - url = f"{base_url}{path}" + url = build_copilot_upstream_url(base_url, path) # Preserve query string parameters if request.url.query: diff --git a/tests/test_cli/test_wrap_copilot.py b/tests/test_cli/test_wrap_copilot.py index dc8bafbab..29105f437 100644 --- a/tests/test_cli/test_wrap_copilot.py +++ b/tests/test_cli/test_wrap_copilot.py @@ -44,13 +44,16 @@ def test_wrap_copilot_auto_anthropic_injects_instructions( def fake_launch_tool(**kwargs): # noqa: ANN003 captured.update(kwargs) - with patch("headroom.cli.wrap.shutil.which", return_value="copilot"): - with patch("headroom.cli.wrap._ensure_rtk_binary", return_value=Path("/tmp/rtk")): - with patch("headroom.cli.wrap._launch_tool", side_effect=fake_launch_tool): - result = runner.invoke( - main, - ["wrap", "copilot", "--", "--model", "claude-sonnet-4-20250514"], - ) + with ( + patch("headroom.cli.wrap.shutil.which", return_value="copilot"), + patch("headroom.cli.wrap.has_oauth_auth", return_value=False), + patch("headroom.cli.wrap._ensure_rtk_binary", return_value=Path("/tmp/rtk")), + patch("headroom.cli.wrap._launch_tool", side_effect=fake_launch_tool), + ): + result = runner.invoke( + main, + ["wrap", "copilot", "--", "--model", "claude-sonnet-4-20250514"], + ) assert result.exit_code == 0, result.output instructions = tmp_path / ".github" / "copilot-instructions.md" @@ -78,25 +81,28 @@ def test_wrap_copilot_openai_backend_sets_completions_env( def fake_launch_tool(**kwargs): # noqa: ANN003 captured.update(kwargs) - with patch("headroom.cli.wrap.shutil.which", return_value="copilot"): - with patch("headroom.cli.wrap._launch_tool", side_effect=fake_launch_tool): - result = runner.invoke( - main, - [ - "wrap", - "copilot", - "--no-rtk", - "--backend", - "anyllm", - "--anyllm-provider", - "groq", - "--region", - "us-central1", - "--", - "--model", - "gpt-4o", - ], - ) + with ( + patch("headroom.cli.wrap.shutil.which", return_value="copilot"), + patch("headroom.cli.wrap.has_oauth_auth", return_value=False), + patch("headroom.cli.wrap._launch_tool", side_effect=fake_launch_tool), + ): + result = runner.invoke( + main, + [ + "wrap", + "copilot", + "--no-rtk", + "--backend", + "anyllm", + "--anyllm-provider", + "groq", + "--region", + "us-central1", + "--", + "--model", + "gpt-4o", + ], + ) assert result.exit_code == 0, result.output @@ -120,14 +126,17 @@ def test_wrap_copilot_auto_detects_running_proxy_backend( def fake_launch_tool(**kwargs): # noqa: ANN003 captured.update(kwargs) - with patch("headroom.cli.wrap.shutil.which", return_value="copilot"): - with patch("headroom.cli.wrap._check_proxy", return_value=True): - with patch("headroom.cli.wrap._detect_running_proxy_backend", return_value="anyllm"): - with patch("headroom.cli.wrap._launch_tool", side_effect=fake_launch_tool): - result = runner.invoke( - main, - ["wrap", "copilot", "--no-rtk", "--", "--model", "gpt-4o"], - ) + with ( + patch("headroom.cli.wrap.shutil.which", return_value="copilot"), + patch("headroom.cli.wrap.has_oauth_auth", return_value=False), + patch("headroom.cli.wrap._check_proxy", return_value=True), + patch("headroom.cli.wrap._detect_running_proxy_backend", return_value="anyllm"), + patch("headroom.cli.wrap._launch_tool", side_effect=fake_launch_tool), + ): + result = runner.invoke( + main, + ["wrap", "copilot", "--no-rtk", "--", "--model", "gpt-4o"], + ) assert result.exit_code == 0, result.output env = captured["env"] @@ -237,16 +246,19 @@ def test_wrap_copilot_clears_stale_wire_api_in_anthropic_mode( def fake_launch_tool(**kwargs): # noqa: ANN003 captured.update(kwargs) - with patch("headroom.cli.wrap.shutil.which", return_value="copilot"): - with patch("headroom.cli.wrap._launch_tool", side_effect=fake_launch_tool): - result = runner.invoke( - main, - ["wrap", "copilot", "--no-rtk", "--", "--model", "claude-sonnet-4-20250514"], - env={ - "COPILOT_PROVIDER_WIRE_API": "responses", - "ANTHROPIC_API_KEY": "sk-test-dummy", - }, - ) + with ( + patch("headroom.cli.wrap.shutil.which", return_value="copilot"), + patch("headroom.cli.wrap.has_oauth_auth", return_value=False), + patch("headroom.cli.wrap._launch_tool", side_effect=fake_launch_tool), + ): + result = runner.invoke( + main, + ["wrap", "copilot", "--no-rtk", "--", "--model", "claude-sonnet-4-20250514"], + env={ + "COPILOT_PROVIDER_WIRE_API": "responses", + "ANTHROPIC_API_KEY": "sk-test-dummy", + }, + ) assert result.exit_code == 0, result.output env = captured["env"] diff --git a/tests/test_copilot_auth.py b/tests/test_copilot_auth.py index 663dc66d3..d3bd94221 100644 --- a/tests/test_copilot_auth.py +++ b/tests/test_copilot_auth.py @@ -1,5 +1,6 @@ from __future__ import annotations +import asyncio import json import time from pathlib import Path @@ -14,6 +15,32 @@ def test_read_cached_oauth_token_prefers_env(monkeypatch: pytest.MonkeyPatch) -> assert copilot_auth.read_cached_oauth_token() == "gho-env" +def test_read_cached_oauth_token_falls_back_to_gh_cli(monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.delenv("GITHUB_COPILOT_GITHUB_TOKEN", raising=False) + monkeypatch.delenv("GITHUB_COPILOT_TOKEN", raising=False) + monkeypatch.delenv("GITHUB_TOKEN", raising=False) + monkeypatch.delenv("COPILOT_GITHUB_TOKEN", raising=False) + monkeypatch.setattr(copilot_auth, "_read_windows_copilot_cli_oauth_token", lambda: None) + monkeypatch.setattr(copilot_auth, "_read_gh_cli_oauth_token", lambda: "gho-gh-cli") + + assert copilot_auth.read_cached_oauth_token() == "gho-gh-cli" + + +def test_read_cached_oauth_token_prefers_copilot_cli_windows_token( + monkeypatch: pytest.MonkeyPatch, +) -> None: + monkeypatch.delenv("GITHUB_COPILOT_GITHUB_TOKEN", raising=False) + monkeypatch.delenv("GITHUB_COPILOT_TOKEN", raising=False) + monkeypatch.delenv("GITHUB_TOKEN", raising=False) + monkeypatch.delenv("COPILOT_GITHUB_TOKEN", raising=False) + monkeypatch.setattr( + copilot_auth, "_read_windows_copilot_cli_oauth_token", lambda: "gho-copilot" + ) + monkeypatch.setattr(copilot_auth, "_read_gh_cli_oauth_token", lambda: "gho-gh-cli") + + assert copilot_auth.read_cached_oauth_token() == "gho-copilot" + + def test_read_cached_oauth_token_reads_hosts_file( monkeypatch: pytest.MonkeyPatch, tmp_path: Path ) -> None: @@ -31,6 +58,8 @@ def test_read_cached_oauth_token_reads_hosts_file( ) monkeypatch.delenv("GITHUB_COPILOT_TOKEN", raising=False) monkeypatch.setenv("GITHUB_COPILOT_TOKEN_FILE", str(hosts)) + monkeypatch.setattr(copilot_auth, "_read_windows_copilot_cli_oauth_token", lambda: None) + monkeypatch.setattr(copilot_auth, "_read_gh_cli_oauth_token", lambda: None) assert copilot_auth.read_cached_oauth_token() == "gho-file" @@ -44,10 +73,33 @@ def test_read_cached_oauth_token_skips_expired_entries( encoding="utf-8", ) monkeypatch.setenv("GITHUB_COPILOT_TOKEN_FILE", str(hosts)) + monkeypatch.setattr(copilot_auth, "_read_windows_copilot_cli_oauth_token", lambda: None) + monkeypatch.setattr(copilot_auth, "_read_gh_cli_oauth_token", lambda: None) assert copilot_auth.read_cached_oauth_token() is None +def test_read_gh_cli_oauth_token_uses_hostname(monkeypatch: pytest.MonkeyPatch) -> None: + calls: list[list[str]] = [] + + class CompletedProcess: + def __init__(self) -> None: + self.returncode = 0 + self.stdout = "gho-gh-cli\n" + + def fake_run(*args: object, **kwargs: object) -> CompletedProcess: + calls.append(list(args[0])) + assert kwargs["capture_output"] is True + assert kwargs["check"] is False + return CompletedProcess() + + monkeypatch.setenv("GITHUB_COPILOT_HOST", "example.ghe.com") + monkeypatch.setattr(copilot_auth.subprocess, "run", fake_run) + + assert copilot_auth._read_gh_cli_oauth_token() == "gho-gh-cli" + assert calls == [["gh", "auth", "token", "--hostname", "example.ghe.com"]] + + def test_resolve_client_bearer_token_prefers_api_token(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("GITHUB_COPILOT_API_TOKEN", "copilot-api") monkeypatch.setenv("GITHUB_COPILOT_TOKEN", "gho-oauth") @@ -61,10 +113,24 @@ def test_is_copilot_api_url_matches_expected_hosts() -> None: assert not copilot_auth.is_copilot_api_url("https://api.openai.com/v1/chat/completions") -@pytest.mark.asyncio -async def test_apply_copilot_api_auth_replaces_authorization( - monkeypatch: pytest.MonkeyPatch, -) -> None: +def test_build_copilot_upstream_url_strips_v1_only_for_copilot_hosts() -> None: + assert ( + copilot_auth.build_copilot_upstream_url( + "https://api.githubcopilot.com", + "/v1/chat/completions", + ) + == "https://api.githubcopilot.com/chat/completions" + ) + assert ( + copilot_auth.build_copilot_upstream_url( + "https://api.openai.com", + "/v1/chat/completions", + ) + == "https://api.openai.com/v1/chat/completions" + ) + + +def test_apply_copilot_api_auth_replaces_authorization(monkeypatch: pytest.MonkeyPatch) -> None: async def fake_get_api_token() -> copilot_auth.CopilotAPIToken: return copilot_auth.CopilotAPIToken( token="copilot-session", @@ -78,17 +144,20 @@ async def test_apply_copilot_api_auth_replaces_authorization( fake_get_api_token, ) - headers = await copilot_auth.apply_copilot_api_auth( - {"authorization": "Bearer downstream-token"}, - url="https://api.githubcopilot.com/v1/chat/completions", + headers = asyncio.run( + copilot_auth.apply_copilot_api_auth( + {"authorization": "Bearer downstream-token"}, + url="https://api.githubcopilot.com/v1/chat/completions", + ) ) assert headers["Authorization"] == "Bearer copilot-session" assert "authorization" not in headers -@pytest.mark.asyncio -async def test_token_provider_exchanges_and_caches(monkeypatch: pytest.MonkeyPatch) -> None: +def test_token_provider_reuses_oauth_token_without_exchange( + monkeypatch: pytest.MonkeyPatch, +) -> None: monkeypatch.setenv("GITHUB_COPILOT_TOKEN", "gho-oauth") provider = copilot_auth.CopilotTokenProvider() @@ -106,8 +175,35 @@ async def test_token_provider_exchanges_and_caches(monkeypatch: pytest.MonkeyPat monkeypatch.setattr(provider, "_exchange_token_sync", staticmethod(fake_exchange)) - first = await provider.get_api_token() - second = await provider.get_api_token() + first = asyncio.run(provider.get_api_token()) + second = asyncio.run(provider.get_api_token()) + + assert first.token == "gho-oauth" + assert second.token == "gho-oauth" + assert calls["count"] == 0 + + +def test_token_provider_can_exchange_when_enabled(monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setenv("GITHUB_COPILOT_TOKEN", "gho-oauth") + monkeypatch.setenv("GITHUB_COPILOT_USE_TOKEN_EXCHANGE", "true") + + provider = copilot_auth.CopilotTokenProvider() + calls = {"count": 0} + + def fake_exchange(headers: dict[str, str]) -> dict[str, object]: + calls["count"] += 1 + return { + "token": "copilot-api", + "expires_at": int(time.time()) + 3600, + "refresh_in": 1200, + "endpoints": {"api": "https://api.githubcopilot.com"}, + "sku": "copilot_individual", + } + + monkeypatch.setattr(provider, "_exchange_token_sync", staticmethod(fake_exchange)) + + first = asyncio.run(provider.get_api_token()) + second = asyncio.run(provider.get_api_token()) assert first.token == "copilot-api" assert second.token == "copilot-api" diff --git a/tests/test_proxy_copilot_auth_hooks.py b/tests/test_proxy_copilot_auth_hooks.py index 5a89d8017..289ab282c 100644 --- a/tests/test_proxy_copilot_auth_hooks.py +++ b/tests/test_proxy_copilot_auth_hooks.py @@ -1,5 +1,6 @@ from __future__ import annotations +import asyncio import importlib.util import sys import types @@ -50,8 +51,7 @@ def _load_handler_module(module_name: str, relative_path: str): return module -@pytest.mark.asyncio -async def test_openai_passthrough_applies_copilot_auth(monkeypatch: pytest.MonkeyPatch) -> None: +def test_openai_passthrough_applies_copilot_auth(monkeypatch: pytest.MonkeyPatch) -> None: openai_mod = _load_handler_module( "tests.headroom_proxy_handlers_openai", "headroom/proxy/handlers/openai.py", @@ -95,20 +95,21 @@ async def test_openai_passthrough_applies_copilot_auth(monkeypatch: pytest.Monke request.body = body handler = Dummy() - response = await handler.handle_passthrough( - request, - "https://api.githubcopilot.com", - "models", - "openai", + response = asyncio.run( + handler.handle_passthrough( + request, + "https://api.githubcopilot.com", + "models", + "openai", + ) ) - assert seen["url"] == "https://api.githubcopilot.com/v1/models" + assert seen["url"] == "https://api.githubcopilot.com/models" assert seen["request_kwargs"]["headers"] == {"Authorization": "Bearer upstream-token"} assert response.status_code == 200 -@pytest.mark.asyncio -async def test_streaming_response_applies_copilot_auth(monkeypatch: pytest.MonkeyPatch) -> None: +def test_streaming_response_applies_copilot_auth(monkeypatch: pytest.MonkeyPatch) -> None: streaming_mod = _load_handler_module( "tests.headroom_proxy_handlers_streaming", "headroom/proxy/handlers/streaming.py", @@ -149,19 +150,21 @@ async def test_streaming_response_applies_copilot_auth(monkeypatch: pytest.Monke return SimpleNamespace(headers={}, status_code=200) handler = Dummy() - response = await handler._stream_response( - url="https://api.githubcopilot.com/v1/responses", - headers={"authorization": "Bearer downstream"}, - body={"model": "gpt-4o"}, - provider="openai", - model="gpt-4o", - request_id="req-test", - original_tokens=0, - optimized_tokens=0, - tokens_saved=0, - transforms_applied=[], - tags={}, - optimization_latency=0.0, + response = asyncio.run( + handler._stream_response( + url="https://api.githubcopilot.com/v1/responses", + headers={"authorization": "Bearer downstream"}, + body={"model": "gpt-4o"}, + provider="openai", + model="gpt-4o", + request_id="req-test", + original_tokens=0, + optimized_tokens=0, + tokens_saved=0, + transforms_applied=[], + tags={}, + optimization_latency=0.0, + ) ) assert seen["url"] == "https://api.githubcopilot.com/v1/responses" From f5b959a470ce2ba518698266884372f5d9f7997b Mon Sep 17 00:00:00 2001 From: JerrettDavis Date: Tue, 21 Apr 2026 23:01:31 -0500 Subject: [PATCH 02/16] test: isolate copilot oauth suites Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- tests/test_cli/test_wrap_copilot.py | 594 +++++++++++++------------ tests/test_proxy_copilot_auth_hooks.py | 25 ++ 2 files changed, 343 insertions(+), 276 deletions(-) diff --git a/tests/test_cli/test_wrap_copilot.py b/tests/test_cli/test_wrap_copilot.py index 29105f437..40d49a559 100644 --- a/tests/test_cli/test_wrap_copilot.py +++ b/tests/test_cli/test_wrap_copilot.py @@ -1,276 +1,318 @@ -"""Tests for `headroom wrap copilot` command.""" - -from __future__ import annotations - -import importlib -import sys -import types -from pathlib import Path -from unittest.mock import patch - -import click -import pytest -from click.testing import CliRunner - -from headroom.copilot_auth import DEFAULT_API_URL - -fake_main_module = types.ModuleType("headroom.cli.main") -fake_main_module.main = click.Group() -sys.modules["headroom.cli.main"] = fake_main_module -sys.modules.pop("headroom.cli", None) -sys.modules.pop("headroom.cli.wrap", None) - -wrap_cli = importlib.import_module("headroom.cli.wrap") -main = fake_main_module.main - - -@pytest.fixture -def runner() -> CliRunner: - return CliRunner() - - -@pytest.fixture(autouse=True) -def no_running_proxy(monkeypatch: pytest.MonkeyPatch) -> None: - monkeypatch.setattr(wrap_cli, "_check_proxy", lambda _port: False) - - -def test_wrap_copilot_auto_anthropic_injects_instructions( - runner: CliRunner, tmp_path: Path, monkeypatch: pytest.MonkeyPatch -) -> None: - monkeypatch.chdir(tmp_path) - monkeypatch.setenv("ANTHROPIC_API_KEY", "sk-test-dummy") - captured: dict[str, object] = {} - - def fake_launch_tool(**kwargs): # noqa: ANN003 - captured.update(kwargs) - - with ( - patch("headroom.cli.wrap.shutil.which", return_value="copilot"), - patch("headroom.cli.wrap.has_oauth_auth", return_value=False), - patch("headroom.cli.wrap._ensure_rtk_binary", return_value=Path("/tmp/rtk")), - patch("headroom.cli.wrap._launch_tool", side_effect=fake_launch_tool), - ): - result = runner.invoke( - main, - ["wrap", "copilot", "--", "--model", "claude-sonnet-4-20250514"], - ) - - assert result.exit_code == 0, result.output - instructions = tmp_path / ".github" / "copilot-instructions.md" - assert instructions.exists() - content = instructions.read_text() - assert wrap_cli._RTK_MARKER in content - assert "RTK (Rust Token Killer)" in content - - env = captured["env"] - assert isinstance(env, dict) - assert env["COPILOT_PROVIDER_TYPE"] == "anthropic" - assert env["COPILOT_PROVIDER_BASE_URL"] == "http://127.0.0.1:8787" - assert "COPILOT_PROVIDER_WIRE_API" not in env - assert captured["agent_type"] == "copilot" - assert captured["tool_label"] == "COPILOT" - assert captured["args"] == ("--model", "claude-sonnet-4-20250514") - - -def test_wrap_copilot_openai_backend_sets_completions_env( - runner: CliRunner, monkeypatch: pytest.MonkeyPatch -) -> None: - monkeypatch.setenv("OPENAI_API_KEY", "sk-test-dummy") - captured: dict[str, object] = {} - - def fake_launch_tool(**kwargs): # noqa: ANN003 - captured.update(kwargs) - - with ( - patch("headroom.cli.wrap.shutil.which", return_value="copilot"), - patch("headroom.cli.wrap.has_oauth_auth", return_value=False), - patch("headroom.cli.wrap._launch_tool", side_effect=fake_launch_tool), - ): - result = runner.invoke( - main, - [ - "wrap", - "copilot", - "--no-rtk", - "--backend", - "anyllm", - "--anyllm-provider", - "groq", - "--region", - "us-central1", - "--", - "--model", - "gpt-4o", - ], - ) - - assert result.exit_code == 0, result.output - - env = captured["env"] - assert isinstance(env, dict) - assert env["COPILOT_PROVIDER_TYPE"] == "openai" - assert env["COPILOT_PROVIDER_BASE_URL"] == "http://127.0.0.1:8787/v1" - assert env["COPILOT_PROVIDER_WIRE_API"] == "completions" - assert captured["backend"] == "anyllm" - assert captured["anyllm_provider"] == "groq" - assert captured["region"] == "us-central1" - assert captured["args"] == ("--model", "gpt-4o") - - -def test_wrap_copilot_auto_detects_running_proxy_backend( - runner: CliRunner, monkeypatch: pytest.MonkeyPatch -) -> None: - monkeypatch.setenv("OPENAI_API_KEY", "sk-test-dummy") - captured: dict[str, object] = {} - - def fake_launch_tool(**kwargs): # noqa: ANN003 - captured.update(kwargs) - - with ( - patch("headroom.cli.wrap.shutil.which", return_value="copilot"), - patch("headroom.cli.wrap.has_oauth_auth", return_value=False), - patch("headroom.cli.wrap._check_proxy", return_value=True), - patch("headroom.cli.wrap._detect_running_proxy_backend", return_value="anyllm"), - patch("headroom.cli.wrap._launch_tool", side_effect=fake_launch_tool), - ): - result = runner.invoke( - main, - ["wrap", "copilot", "--no-rtk", "--", "--model", "gpt-4o"], - ) - - assert result.exit_code == 0, result.output - env = captured["env"] - assert isinstance(env, dict) - assert env["COPILOT_PROVIDER_TYPE"] == "openai" - assert env["COPILOT_PROVIDER_BASE_URL"] == "http://127.0.0.1:8787/v1" - assert env["COPILOT_PROVIDER_WIRE_API"] == "completions" - - -def test_wrap_copilot_prefers_existing_oauth_session( - runner: CliRunner, monkeypatch: pytest.MonkeyPatch -) -> None: - monkeypatch.setenv("ANTHROPIC_API_KEY", "sk-test-dummy") - captured: dict[str, object] = {} - - def fake_launch_tool(**kwargs): # noqa: ANN003 - captured.update(kwargs) - - with patch("headroom.cli.wrap.shutil.which", return_value="copilot"): - with patch("headroom.cli.wrap.resolve_client_bearer_token", return_value="gho-existing"): - with patch("headroom.cli.wrap.has_oauth_auth", return_value=True): - with patch("headroom.cli.wrap._launch_tool", side_effect=fake_launch_tool): - result = runner.invoke( - main, - ["wrap", "copilot", "--no-rtk", "--", "--model", "claude-sonnet-4.6"], - ) - - assert result.exit_code == 0, result.output - env = captured["env"] - assert isinstance(env, dict) - assert env["COPILOT_PROVIDER_TYPE"] == "openai" - assert env["COPILOT_PROVIDER_BASE_URL"] == "http://127.0.0.1:8787/v1" - assert env["COPILOT_PROVIDER_WIRE_API"] == "completions" - assert env["COPILOT_PROVIDER_BEARER_TOKEN"] == "gho-existing" - assert "COPILOT_PROVIDER_API_KEY" not in env - assert captured["openai_api_url"] == DEFAULT_API_URL - - -def test_wrap_copilot_translated_backend_still_requires_byok( - runner: CliRunner, -) -> None: - with patch("headroom.cli.wrap.shutil.which", return_value="copilot"): - with patch("headroom.cli.wrap.has_oauth_auth", return_value=True): - result = runner.invoke( - main, - [ - "wrap", - "copilot", - "--backend", - "anyllm", - "--", - "--model", - "gpt-4o", - ], - ) - - assert result.exit_code == 1 - assert "Copilot BYOK mode requires a provider API key" in result.output - - -def test_wrap_copilot_rejects_wire_api_for_anthropic_provider(runner: CliRunner) -> None: - with patch("headroom.cli.wrap.shutil.which", return_value="copilot"): - result = runner.invoke( - main, - [ - "wrap", - "copilot", - "--wire-api", - "responses", - "--", - "--model", - "claude-sonnet-4-20250514", - ], - ) - - assert result.exit_code != 0 - assert "--wire-api is only valid" in result.output - - -def test_wrap_copilot_rejects_responses_for_translated_backends(runner: CliRunner) -> None: - with patch("headroom.cli.wrap.shutil.which", return_value="copilot"): - result = runner.invoke( - main, - [ - "wrap", - "copilot", - "--backend", - "anyllm", - "--wire-api", - "responses", - "--", - "--model", - "gpt-4o", - ], - ) - - assert result.exit_code != 0 - assert "not supported with translated backends" in result.output - - -def test_wrap_copilot_clears_stale_wire_api_in_anthropic_mode( - runner: CliRunner, monkeypatch: pytest.MonkeyPatch -) -> None: - monkeypatch.setenv("ANTHROPIC_API_KEY", "sk-test-dummy") - captured: dict[str, object] = {} - - def fake_launch_tool(**kwargs): # noqa: ANN003 - captured.update(kwargs) - - with ( - patch("headroom.cli.wrap.shutil.which", return_value="copilot"), - patch("headroom.cli.wrap.has_oauth_auth", return_value=False), - patch("headroom.cli.wrap._launch_tool", side_effect=fake_launch_tool), - ): - result = runner.invoke( - main, - ["wrap", "copilot", "--no-rtk", "--", "--model", "claude-sonnet-4-20250514"], - env={ - "COPILOT_PROVIDER_WIRE_API": "responses", - "ANTHROPIC_API_KEY": "sk-test-dummy", - }, - ) - - assert result.exit_code == 0, result.output - env = captured["env"] - assert isinstance(env, dict) - assert env["COPILOT_PROVIDER_TYPE"] == "anthropic" - assert "COPILOT_PROVIDER_WIRE_API" not in env - - -def test_wrap_copilot_fails_when_binary_missing(runner: CliRunner) -> None: - with patch("headroom.cli.wrap.shutil.which", return_value=None): - result = runner.invoke(main, ["wrap", "copilot", "--", "--model", "gpt-4o"]) - - assert result.exit_code == 1 - assert "'copilot' not found in PATH" in result.output - assert "Install GitHub Copilot CLI" in result.output +"""Tests for `headroom wrap copilot` command.""" + +from __future__ import annotations + +import importlib +import sys +import types +from pathlib import Path +from unittest.mock import patch + +import click +import pytest +from click.testing import CliRunner + +from headroom.copilot_auth import DEFAULT_API_URL + + +@pytest.fixture +def runner() -> CliRunner: + return CliRunner() + + +@pytest.fixture +def wrap_modules(monkeypatch: pytest.MonkeyPatch) -> tuple[types.ModuleType, click.Group]: + saved_modules = { + name: sys.modules.get(name) + for name in ("headroom.cli", "headroom.cli.main", "headroom.cli.wrap") + } + + fake_main_module = types.ModuleType("headroom.cli.main") + fake_main_module.main = click.Group() + sys.modules["headroom.cli.main"] = fake_main_module + sys.modules.pop("headroom.cli", None) + sys.modules.pop("headroom.cli.wrap", None) + + wrap_cli = importlib.import_module("headroom.cli.wrap") + monkeypatch.setattr(wrap_cli, "_check_proxy", lambda _port: False) + + try: + yield wrap_cli, fake_main_module.main + finally: + for name in ("headroom.cli.wrap", "headroom.cli.main", "headroom.cli"): + sys.modules.pop(name, None) + for name, module in saved_modules.items(): + if module is not None: + sys.modules[name] = module + + +def test_wrap_copilot_auto_anthropic_injects_instructions( + runner: CliRunner, + wrap_modules: tuple[types.ModuleType, click.Group], + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, +) -> None: + wrap_cli, main = wrap_modules + monkeypatch.chdir(tmp_path) + monkeypatch.setenv("ANTHROPIC_API_KEY", "sk-test-dummy") + captured: dict[str, object] = {} + + def fake_launch_tool(**kwargs): # noqa: ANN003 + captured.update(kwargs) + + with ( + patch("headroom.cli.wrap.shutil.which", return_value="copilot"), + patch("headroom.cli.wrap.has_oauth_auth", return_value=False), + patch("headroom.cli.wrap._ensure_rtk_binary", return_value=Path("/tmp/rtk")), + patch("headroom.cli.wrap._launch_tool", side_effect=fake_launch_tool), + ): + result = runner.invoke( + main, + ["wrap", "copilot", "--", "--model", "claude-sonnet-4-20250514"], + ) + + assert result.exit_code == 0, result.output + instructions = tmp_path / ".github" / "copilot-instructions.md" + assert instructions.exists() + content = instructions.read_text() + assert wrap_cli._RTK_MARKER in content + assert "RTK (Rust Token Killer)" in content + + env = captured["env"] + assert isinstance(env, dict) + assert env["COPILOT_PROVIDER_TYPE"] == "anthropic" + assert env["COPILOT_PROVIDER_BASE_URL"] == "http://127.0.0.1:8787" + assert "COPILOT_PROVIDER_WIRE_API" not in env + assert captured["agent_type"] == "copilot" + assert captured["tool_label"] == "COPILOT" + assert captured["args"] == ("--model", "claude-sonnet-4-20250514") + + +def test_wrap_copilot_openai_backend_sets_completions_env( + runner: CliRunner, + wrap_modules: tuple[types.ModuleType, click.Group], + monkeypatch: pytest.MonkeyPatch, +) -> None: + _wrap_cli, main = wrap_modules + monkeypatch.setenv("OPENAI_API_KEY", "sk-test-dummy") + captured: dict[str, object] = {} + + def fake_launch_tool(**kwargs): # noqa: ANN003 + captured.update(kwargs) + + with ( + patch("headroom.cli.wrap.shutil.which", return_value="copilot"), + patch("headroom.cli.wrap.has_oauth_auth", return_value=False), + patch("headroom.cli.wrap._launch_tool", side_effect=fake_launch_tool), + ): + result = runner.invoke( + main, + [ + "wrap", + "copilot", + "--no-rtk", + "--backend", + "anyllm", + "--anyllm-provider", + "groq", + "--region", + "us-central1", + "--", + "--model", + "gpt-4o", + ], + ) + + assert result.exit_code == 0, result.output + + env = captured["env"] + assert isinstance(env, dict) + assert env["COPILOT_PROVIDER_TYPE"] == "openai" + assert env["COPILOT_PROVIDER_BASE_URL"] == "http://127.0.0.1:8787/v1" + assert env["COPILOT_PROVIDER_WIRE_API"] == "completions" + assert captured["backend"] == "anyllm" + assert captured["anyllm_provider"] == "groq" + assert captured["region"] == "us-central1" + assert captured["args"] == ("--model", "gpt-4o") + + +def test_wrap_copilot_auto_detects_running_proxy_backend( + runner: CliRunner, + wrap_modules: tuple[types.ModuleType, click.Group], + monkeypatch: pytest.MonkeyPatch, +) -> None: + _wrap_cli, main = wrap_modules + monkeypatch.setenv("OPENAI_API_KEY", "sk-test-dummy") + captured: dict[str, object] = {} + + def fake_launch_tool(**kwargs): # noqa: ANN003 + captured.update(kwargs) + + with ( + patch("headroom.cli.wrap.shutil.which", return_value="copilot"), + patch("headroom.cli.wrap.has_oauth_auth", return_value=False), + patch("headroom.cli.wrap._check_proxy", return_value=True), + patch("headroom.cli.wrap._detect_running_proxy_backend", return_value="anyllm"), + patch("headroom.cli.wrap._launch_tool", side_effect=fake_launch_tool), + ): + result = runner.invoke( + main, + ["wrap", "copilot", "--no-rtk", "--", "--model", "gpt-4o"], + ) + + assert result.exit_code == 0, result.output + env = captured["env"] + assert isinstance(env, dict) + assert env["COPILOT_PROVIDER_TYPE"] == "openai" + assert env["COPILOT_PROVIDER_BASE_URL"] == "http://127.0.0.1:8787/v1" + assert env["COPILOT_PROVIDER_WIRE_API"] == "completions" + + +def test_wrap_copilot_prefers_existing_oauth_session( + runner: CliRunner, + wrap_modules: tuple[types.ModuleType, click.Group], + monkeypatch: pytest.MonkeyPatch, +) -> None: + _wrap_cli, main = wrap_modules + monkeypatch.setenv("ANTHROPIC_API_KEY", "sk-test-dummy") + captured: dict[str, object] = {} + + def fake_launch_tool(**kwargs): # noqa: ANN003 + captured.update(kwargs) + + with patch("headroom.cli.wrap.shutil.which", return_value="copilot"): + with patch("headroom.cli.wrap.resolve_client_bearer_token", return_value="gho-existing"): + with patch("headroom.cli.wrap.has_oauth_auth", return_value=True): + with patch("headroom.cli.wrap._launch_tool", side_effect=fake_launch_tool): + result = runner.invoke( + main, + ["wrap", "copilot", "--no-rtk", "--", "--model", "claude-sonnet-4.6"], + ) + + assert result.exit_code == 0, result.output + env = captured["env"] + assert isinstance(env, dict) + assert env["COPILOT_PROVIDER_TYPE"] == "openai" + assert env["COPILOT_PROVIDER_BASE_URL"] == "http://127.0.0.1:8787/v1" + assert env["COPILOT_PROVIDER_WIRE_API"] == "completions" + assert env["COPILOT_PROVIDER_BEARER_TOKEN"] == "gho-existing" + assert "COPILOT_PROVIDER_API_KEY" not in env + assert captured["openai_api_url"] == DEFAULT_API_URL + + +def test_wrap_copilot_translated_backend_still_requires_byok( + runner: CliRunner, + wrap_modules: tuple[types.ModuleType, click.Group], +) -> None: + _wrap_cli, main = wrap_modules + with patch("headroom.cli.wrap.shutil.which", return_value="copilot"): + with patch("headroom.cli.wrap.has_oauth_auth", return_value=True): + result = runner.invoke( + main, + [ + "wrap", + "copilot", + "--backend", + "anyllm", + "--", + "--model", + "gpt-4o", + ], + ) + + assert result.exit_code == 1 + assert "Copilot BYOK mode requires a provider API key" in result.output + + +def test_wrap_copilot_rejects_wire_api_for_anthropic_provider( + runner: CliRunner, + wrap_modules: tuple[types.ModuleType, click.Group], +) -> None: + _wrap_cli, main = wrap_modules + with patch("headroom.cli.wrap.shutil.which", return_value="copilot"): + result = runner.invoke( + main, + [ + "wrap", + "copilot", + "--wire-api", + "responses", + "--", + "--model", + "claude-sonnet-4-20250514", + ], + ) + + assert result.exit_code != 0 + assert "--wire-api is only valid" in result.output + + +def test_wrap_copilot_rejects_responses_for_translated_backends( + runner: CliRunner, + wrap_modules: tuple[types.ModuleType, click.Group], +) -> None: + _wrap_cli, main = wrap_modules + with patch("headroom.cli.wrap.shutil.which", return_value="copilot"): + result = runner.invoke( + main, + [ + "wrap", + "copilot", + "--backend", + "anyllm", + "--wire-api", + "responses", + "--", + "--model", + "gpt-4o", + ], + ) + + assert result.exit_code != 0 + assert "not supported with translated backends" in result.output + + +def test_wrap_copilot_clears_stale_wire_api_in_anthropic_mode( + runner: CliRunner, + wrap_modules: tuple[types.ModuleType, click.Group], + monkeypatch: pytest.MonkeyPatch, +) -> None: + _wrap_cli, main = wrap_modules + monkeypatch.setenv("ANTHROPIC_API_KEY", "sk-test-dummy") + captured: dict[str, object] = {} + + def fake_launch_tool(**kwargs): # noqa: ANN003 + captured.update(kwargs) + + with ( + patch("headroom.cli.wrap.shutil.which", return_value="copilot"), + patch("headroom.cli.wrap.has_oauth_auth", return_value=False), + patch("headroom.cli.wrap._launch_tool", side_effect=fake_launch_tool), + ): + result = runner.invoke( + main, + ["wrap", "copilot", "--no-rtk", "--", "--model", "claude-sonnet-4-20250514"], + env={ + "COPILOT_PROVIDER_WIRE_API": "responses", + "ANTHROPIC_API_KEY": "sk-test-dummy", + }, + ) + + assert result.exit_code == 0, result.output + env = captured["env"] + assert isinstance(env, dict) + assert env["COPILOT_PROVIDER_TYPE"] == "anthropic" + assert "COPILOT_PROVIDER_WIRE_API" not in env + + +def test_wrap_copilot_fails_when_binary_missing( + runner: CliRunner, + wrap_modules: tuple[types.ModuleType, click.Group], +) -> None: + _wrap_cli, main = wrap_modules + with patch("headroom.cli.wrap.shutil.which", return_value=None): + result = runner.invoke(main, ["wrap", "copilot", "--", "--model", "gpt-4o"]) + + assert result.exit_code == 1 + assert "'copilot' not found in PATH" in result.output + assert "Install GitHub Copilot CLI" in result.output diff --git a/tests/test_proxy_copilot_auth_hooks.py b/tests/test_proxy_copilot_auth_hooks.py index 289ab282c..99a98dabd 100644 --- a/tests/test_proxy_copilot_auth_hooks.py +++ b/tests/test_proxy_copilot_auth_hooks.py @@ -10,6 +10,27 @@ from types import SimpleNamespace import pytest ROOT = Path(__file__).resolve().parents[1] +_ISOLATED_MODULE_NAMES = ( + "headroom.proxy", + "headroom.proxy.handlers", + "httpx", + "fastapi.responses", + "tests.headroom_proxy_handlers_openai", + "tests.headroom_proxy_handlers_streaming", +) + + +@pytest.fixture(autouse=True) +def restore_isolated_modules() -> None: + saved_modules = {name: sys.modules.get(name) for name in _ISOLATED_MODULE_NAMES} + try: + yield + finally: + for name in _ISOLATED_MODULE_NAMES: + sys.modules.pop(name, None) + for name, module in saved_modules.items(): + if module is not None: + sys.modules[name] = module def _load_handler_module(module_name: str, relative_path: str): @@ -39,8 +60,12 @@ def _load_handler_module(module_name: str, relative_path: str): class StreamingResponse(Response): pass + class JSONResponse(Response): + pass + responses_mod.Response = Response responses_mod.StreamingResponse = StreamingResponse + responses_mod.JSONResponse = JSONResponse sys.modules["fastapi.responses"] = responses_mod spec = importlib.util.spec_from_file_location(module_name, ROOT / relative_path) From e5cad5ed920b9e1f2e189b94ebf9aa19da27f6d0 Mon Sep 17 00:00:00 2001 From: JerrettDavis Date: Tue, 21 Apr 2026 23:10:21 -0500 Subject: [PATCH 03/16] fix: satisfy cross-platform mypy for copilot auth Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- headroom/copilot_auth.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/headroom/copilot_auth.py b/headroom/copilot_auth.py index a4540bd00..4f8c8a577 100644 --- a/headroom/copilot_auth.py +++ b/headroom/copilot_auth.py @@ -148,7 +148,11 @@ def _read_windows_copilot_cli_oauth_token() -> str | None: cred_ptr = ctypes.POINTER(CREDENTIAL) credentials = ctypes.POINTER(cred_ptr)() count = wintypes.DWORD() - advapi32 = ctypes.WinDLL("Advapi32.dll") + win_dll = getattr(ctypes, "WinDLL", None) + if win_dll is None: + return None + + advapi32 = win_dll("Advapi32.dll") advapi32.CredEnumerateW.argtypes = [ wintypes.LPCWSTR, wintypes.DWORD, From af784465df6a82b2dc7b1ba7890bc8cb5ca838d1 Mon Sep 17 00:00:00 2001 From: JerrettDavis Date: Tue, 21 Apr 2026 23:23:58 -0500 Subject: [PATCH 04/16] test: restore cli package state Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- tests/test_cli/test_wrap_copilot.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/test_cli/test_wrap_copilot.py b/tests/test_cli/test_wrap_copilot.py index 40d49a559..15722c58e 100644 --- a/tests/test_cli/test_wrap_copilot.py +++ b/tests/test_cli/test_wrap_copilot.py @@ -22,6 +22,10 @@ def runner() -> CliRunner: @pytest.fixture def wrap_modules(monkeypatch: pytest.MonkeyPatch) -> tuple[types.ModuleType, click.Group]: + headroom_pkg = sys.modules.get("headroom") + saved_headroom_cli_attr = ( + headroom_pkg.cli if headroom_pkg is not None and hasattr(headroom_pkg, "cli") else None + ) saved_modules = { name: sys.modules.get(name) for name in ("headroom.cli", "headroom.cli.main", "headroom.cli.wrap") @@ -44,6 +48,18 @@ def wrap_modules(monkeypatch: pytest.MonkeyPatch) -> tuple[types.ModuleType, cli for name, module in saved_modules.items(): if module is not None: sys.modules[name] = module + if saved_modules["headroom.cli"] is not None: + cli_pkg = saved_modules["headroom.cli"] + if saved_modules["headroom.cli.main"] is not None: + cli_pkg.main = saved_modules["headroom.cli.main"] + if saved_modules["headroom.cli.wrap"] is not None: + cli_pkg.wrap = saved_modules["headroom.cli.wrap"] + if headroom_pkg is not None: + if saved_headroom_cli_attr is None: + if hasattr(headroom_pkg, "cli"): + delattr(headroom_pkg, "cli") + else: + headroom_pkg.cli = saved_headroom_cli_attr def test_wrap_copilot_auto_anthropic_injects_instructions( From 0a8a6dca1eecf88419d4a5e3c6505211cb6e97c4 Mon Sep 17 00:00:00 2001 From: JerrettDavis Date: Tue, 21 Apr 2026 23:32:10 -0500 Subject: [PATCH 05/16] test: derive canonical release version Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- tests/test_release_version.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_release_version.py b/tests/test_release_version.py index 52bb0fcac..461e0db45 100644 --- a/tests/test_release_version.py +++ b/tests/test_release_version.py @@ -14,6 +14,7 @@ from headroom.release_version import ( compute_release_version, determine_bump_level, find_latest_release_tag, + get_canonical_version, list_release_commits, normalize_release_tag, parse_release_tag, @@ -177,7 +178,7 @@ def test_release_version_script_runs_directly_without_importing_headroom_package assert output_path.read_text(encoding="utf-8").splitlines() == [ "version=0.6.0", "npm_version=0.6.0", - "canonical=0.5.25", + f"canonical={get_canonical_version(ROOT)}", "height=0", "bump=manual", "previous_tag=", From 94cf57ac4d0b81a6f9a3f9ec40f0fe298ae8ca1f Mon Sep 17 00:00:00 2001 From: JerrettDavis Date: Tue, 21 Apr 2026 23:38:01 -0500 Subject: [PATCH 06/16] test: sync release workflow assertions Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- tests/test_release_workflows.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/test_release_workflows.py b/tests/test_release_workflows.py index 16cdfc3d2..c753ff882 100644 --- a/tests/test_release_workflows.py +++ b/tests/test_release_workflows.py @@ -27,6 +27,17 @@ def test_release_workflow_publishes_both_node_packages_to_github_packages() -> N assert "SDK_TARBALL: ${{ steps.gpr-sdk-publish.outputs.unscoped_sdk_tarball }}" in content +def test_release_workflow_publishes_python_distributions_to_github_release() -> None: + content = (ROOT / ".github" / "workflows" / "release.yml").read_text(encoding="utf-8") + + assert "Publish ${{ env.PYPI_PACKAGE }} Python distributions to GitHub Release" in content + assert ( + 'gh release upload "$TAG" release-assets/*.whl release-assets/*.tar.gz --clobber' in content + ) + assert "Publish Node package tarballs to GitHub Release" in content + assert 'gh release upload "$TAG" release-assets/*.tgz --clobber' in content + + def test_create_release_runs_after_successful_build_even_if_other_publishes_fail() -> None: content = (ROOT / ".github" / "workflows" / "release.yml").read_text(encoding="utf-8") @@ -38,6 +49,12 @@ def test_create_release_runs_after_successful_build_even_if_other_publishes_fail assert "needs.build.result == 'success'" in content +def test_macos_native_wrapper_dependency_install_retries_pypi_downloads() -> None: + content = (ROOT / ".github" / "workflows" / "ci.yml").read_text(encoding="utf-8") + + assert "python -m pip install --retries 10 --timeout 60 pytest" in content + + def test_ci_commitlint_skips_default_github_merge_commits() -> None: content = (ROOT / ".github" / "workflows" / "ci.yml").read_text(encoding="utf-8") From 64fe9763f5afbe2f5c767535dc56a62c0fa897f6 Mon Sep 17 00:00:00 2001 From: JerrettDavis Date: Tue, 21 Apr 2026 23:53:30 -0500 Subject: [PATCH 07/16] test: skip rtk in BYOK copilot assertion Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- tests/test_cli/test_wrap_copilot.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_cli/test_wrap_copilot.py b/tests/test_cli/test_wrap_copilot.py index 15722c58e..79ddbe342 100644 --- a/tests/test_cli/test_wrap_copilot.py +++ b/tests/test_cli/test_wrap_copilot.py @@ -228,6 +228,7 @@ def test_wrap_copilot_translated_backend_still_requires_byok( [ "wrap", "copilot", + "--no-rtk", "--backend", "anyllm", "--", From 7cfc891317e86bcf080d745663df5250d6b229db Mon Sep 17 00:00:00 2001 From: JerrettDavis Date: Wed, 22 Apr 2026 00:05:47 -0500 Subject: [PATCH 08/16] ci: scope codecov patch coverage Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/copilot-instructions.md | 42 --------------------------------- codecov.yml | 19 +++++++++++++++ 2 files changed, 19 insertions(+), 42 deletions(-) delete mode 100644 .github/copilot-instructions.md create mode 100644 codecov.yml diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md deleted file mode 100644 index 4b79c3973..000000000 --- a/.github/copilot-instructions.md +++ /dev/null @@ -1,42 +0,0 @@ - -# RTK (Rust Token Killer) - Token-Optimized Commands - -When running shell commands, **always prefix with `rtk`**. This reduces context -usage by 60-90% with zero behavior change. If rtk has no filter for a command, -it passes through unchanged so it is always safe to use. - -## Key Commands -```bash -# Git (59-80% savings) -rtk git status rtk git diff rtk git log - -# Files & Search (60-75% savings) -rtk ls rtk read rtk grep -rtk find rtk diff - -# Test (90-99% savings) shows failures only -rtk pytest tests/ rtk cargo test rtk test - -# Build & Lint (80-90% savings) shows errors only -rtk tsc rtk lint rtk cargo build -rtk prettier --check rtk mypy rtk ruff check - -# Analysis (70-90% savings) -rtk err rtk log rtk json -rtk summary rtk deps rtk env - -# GitHub (26-87% savings) -rtk gh pr view rtk gh run list rtk gh issue list - -# Infrastructure (85% savings) -rtk docker ps rtk kubectl get rtk docker logs - -# Package managers (70-90% savings) -rtk pip list rtk pnpm install rtk npm run