69 lines
2.0 KiB
Python
69 lines
2.0 KiB
Python
"""GitHub token retrieval for the GitHub Copilot API.
|
|
|
|
Uses the ``gh`` CLI exclusively — no explicit API token or env var.
|
|
Run ``gh auth login`` once to authenticate; this module handles the rest.
|
|
"""
|
|
|
|
import subprocess
|
|
from typing import Optional
|
|
|
|
|
|
def get_github_token() -> Optional[str]:
|
|
"""Return a GitHub token obtained via the GitHub CLI (``gh auth token``).
|
|
|
|
Returns None if the CLI is unavailable or the user is not logged in.
|
|
"""
|
|
try:
|
|
result = subprocess.run(
|
|
["gh", "auth", "token"],
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=5,
|
|
)
|
|
if result.returncode == 0 and result.stdout.strip():
|
|
return result.stdout.strip()
|
|
except (FileNotFoundError, subprocess.TimeoutExpired):
|
|
pass
|
|
return None
|
|
|
|
|
|
def get_copilot_api_url() -> str:
|
|
"""Resolve the Copilot inference base URL.
|
|
|
|
Queries the GitHub GraphQL API for the user's Copilot endpoints.
|
|
Falls back to the standard individual endpoint on failure.
|
|
"""
|
|
import requests
|
|
|
|
token = get_github_token()
|
|
if not token:
|
|
return "https://api.individual.githubcopilot.com"
|
|
|
|
try:
|
|
resp = requests.post(
|
|
"https://api.github.com/graphql",
|
|
headers={
|
|
"Authorization": f"Bearer {token}",
|
|
"Content-Type": "application/json",
|
|
},
|
|
json={"query": "{ viewer { copilotEndpoints { api } } }"},
|
|
timeout=5,
|
|
)
|
|
if resp.status_code == 200:
|
|
api = resp.json()["data"]["viewer"]["copilotEndpoints"]["api"]
|
|
if api:
|
|
return api.rstrip("/")
|
|
except Exception:
|
|
pass
|
|
|
|
return "https://api.individual.githubcopilot.com"
|
|
|
|
|
|
# Required headers for the Copilot inference API (reverse-engineered from the
|
|
# Copilot CLI at /usr/local/lib/node_modules/@github/copilot).
|
|
COPILOT_HEADERS = {
|
|
"Copilot-Integration-Id": "copilot-developer-cli",
|
|
"X-GitHub-Api-Version": "2025-05-01",
|
|
"Openai-Intent": "conversation-agent",
|
|
}
|