mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-27 01:11:40 +00:00
feat: proper Copilot auth with OAuth device code flow and token validation
Builds on PR #1879's Copilot integration with critical auth improvements modeled after opencode's implementation: - Add hermes_cli/copilot_auth.py with: - OAuth device code flow (copilot_device_code_login) using the same client_id (Ov23li8tweQw6odWQebz) as opencode and Copilot CLI - Token type validation: reject classic PATs (ghp_*) with a clear error message explaining supported token types - Proper env var priority: COPILOT_GITHUB_TOKEN > GH_TOKEN > GITHUB_TOKEN (matching Copilot CLI documentation) - copilot_request_headers() with Openai-Intent, x-initiator, and Copilot-Vision-Request headers (matching opencode) - Update auth.py: - PROVIDER_REGISTRY copilot entry uses correct env var order - _resolve_api_key_provider_secret delegates to copilot_auth for the copilot provider with proper token validation - Update models.py: - copilot_default_headers() now includes Openai-Intent and x-initiator - Update main.py: - _model_flow_copilot offers OAuth device code login when no token is found, with manual token entry as fallback - Shows supported vs unsupported token types - 22 new tests covering token validation, env var priority, header generation, and integration with existing auth infrastructure
This commit is contained in:
parent
8422196e89
commit
21c45ba0ac
6 changed files with 563 additions and 26 deletions
|
|
@ -59,7 +59,7 @@ class TestProviderRegistry:
|
|||
|
||||
def test_copilot_env_vars(self):
|
||||
pconfig = PROVIDER_REGISTRY["copilot"]
|
||||
assert pconfig.api_key_env_vars == ("GITHUB_TOKEN", "GH_TOKEN")
|
||||
assert pconfig.api_key_env_vars == ("COPILOT_GITHUB_TOKEN", "GH_TOKEN", "GITHUB_TOKEN")
|
||||
assert pconfig.base_url_env_var == ""
|
||||
|
||||
def test_kimi_env_vars(self):
|
||||
|
|
@ -279,7 +279,7 @@ class TestApiKeyProviderStatus:
|
|||
assert status["base_url"] == "https://custom.kimi.example/v1"
|
||||
|
||||
def test_copilot_status_uses_gh_cli_token(self, monkeypatch):
|
||||
monkeypatch.setattr("hermes_cli.auth._try_gh_cli_token", lambda: "gh-cli-token")
|
||||
monkeypatch.setattr("hermes_cli.copilot_auth._try_gh_cli_token", lambda: "gho_gh_cli_token")
|
||||
status = get_api_key_provider_status("copilot")
|
||||
assert status["configured"] is True
|
||||
assert status["logged_in"] is True
|
||||
|
|
@ -341,10 +341,10 @@ class TestResolveApiKeyProviderCredentials:
|
|||
assert creds["source"] == "GITHUB_TOKEN"
|
||||
|
||||
def test_resolve_copilot_with_gh_cli_fallback(self, monkeypatch):
|
||||
monkeypatch.setattr("hermes_cli.auth._try_gh_cli_token", lambda: "gh-cli-secret")
|
||||
monkeypatch.setattr("hermes_cli.copilot_auth._try_gh_cli_token", lambda: "gho_cli_secret")
|
||||
creds = resolve_api_key_provider_credentials("copilot")
|
||||
assert creds["provider"] == "copilot"
|
||||
assert creds["api_key"] == "gh-cli-secret"
|
||||
assert creds["api_key"] == "gho_cli_secret"
|
||||
assert creds["base_url"] == "https://api.githubcopilot.com"
|
||||
assert creds["source"] == "gh auth token"
|
||||
|
||||
|
|
@ -515,16 +515,16 @@ class TestRuntimeProviderResolution:
|
|||
assert result["api_key"] == "auto-kimi-key"
|
||||
|
||||
def test_runtime_copilot_uses_gh_cli_token(self, monkeypatch):
|
||||
monkeypatch.setattr("hermes_cli.auth._try_gh_cli_token", lambda: "gh-cli-secret")
|
||||
monkeypatch.setattr("hermes_cli.copilot_auth._try_gh_cli_token", lambda: "gho_cli_secret")
|
||||
from hermes_cli.runtime_provider import resolve_runtime_provider
|
||||
result = resolve_runtime_provider(requested="copilot")
|
||||
assert result["provider"] == "copilot"
|
||||
assert result["api_mode"] == "chat_completions"
|
||||
assert result["api_key"] == "gh-cli-secret"
|
||||
assert result["api_key"] == "gho_cli_secret"
|
||||
assert result["base_url"] == "https://api.githubcopilot.com"
|
||||
|
||||
def test_runtime_copilot_uses_responses_for_gpt_5_4(self, monkeypatch):
|
||||
monkeypatch.setattr("hermes_cli.auth._try_gh_cli_token", lambda: "gh-cli-secret")
|
||||
monkeypatch.setattr("hermes_cli.copilot_auth._try_gh_cli_token", lambda: "gho_cli_secret")
|
||||
monkeypatch.setattr(
|
||||
"hermes_cli.runtime_provider._get_model_config",
|
||||
lambda: {"provider": "copilot", "default": "gpt-5.4"},
|
||||
|
|
@ -590,7 +590,7 @@ class TestHasAnyProviderConfigured:
|
|||
|
||||
def test_gh_cli_token_counts(self, monkeypatch, tmp_path):
|
||||
from hermes_cli import config as config_module
|
||||
monkeypatch.setattr("hermes_cli.auth._try_gh_cli_token", lambda: "gh-cli-secret")
|
||||
monkeypatch.setattr("hermes_cli.copilot_auth._try_gh_cli_token", lambda: "gho_cli_secret")
|
||||
hermes_home = tmp_path / ".hermes"
|
||||
hermes_home.mkdir()
|
||||
monkeypatch.setattr(config_module, "get_env_path", lambda: hermes_home / ".env")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue