From 94e60249044289c4e66312f83e583e05746c45c1 Mon Sep 17 00:00:00 2001 From: Robert Muir Date: Fri, 5 Sep 2025 10:51:40 -0400 Subject: [PATCH] chore(copilot): use GH_HOST for Copilot endpoints and token lookup Update Copilot HTTP requests and token lookup to respect the `GH_HOST` env var, allowing use with GitHub Enterprise instances. Some networks block the `github.com` endpoints, so they won't work at all. Provide a way for the user to hit different endpoint other than `github.com` (e.g. mycorp.ghe.com) without patching the source code. The `GH_HOST` environment variable is the same one used to override this for `gh` cli: https://cli.github.com/manual/gh_help_environment --- lua/codecompanion/adapters/http/copilot/init.lua | 7 +++++-- lua/codecompanion/adapters/http/copilot/stats.lua | 11 ++++++++++- lua/codecompanion/adapters/http/copilot/token.lua | 13 +++++++++++-- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/lua/codecompanion/adapters/http/copilot/init.lua b/lua/codecompanion/adapters/http/copilot/init.lua index a9cdf8038..ed3259739 100644 --- a/lua/codecompanion/adapters/http/copilot/init.lua +++ b/lua/codecompanion/adapters/http/copilot/init.lua @@ -34,8 +34,11 @@ end ---@return table local function handlers(adapter) local model_opts = resolve_model_opts(adapter) + local current_url = adapter.url or "https://api.githubcopilot.com/chat/completions" + local base_url = current_url:gsub("/chat/completions$", ""):gsub("/responses$", "") + if model_opts.endpoint == "responses" then - adapter.url = "https://api.githubcopilot.com/responses" + adapter.url = base_url .. "/responses" local responses = require("codecompanion.adapters.http.openai_responses") @@ -74,7 +77,7 @@ local function handlers(adapter) return responses.handlers end - adapter.url = "https://api.githubcopilot.com/chat/completions" + adapter.url = base_url .. "/chat/completions" return require("codecompanion.adapters.http.openai").handlers end diff --git a/lua/codecompanion/adapters/http/copilot/stats.lua b/lua/codecompanion/adapters/http/copilot/stats.lua index 26cb834d5..64207ecec 100644 --- a/lua/codecompanion/adapters/http/copilot/stats.lua +++ b/lua/codecompanion/adapters/http/copilot/stats.lua @@ -27,8 +27,17 @@ local function get_statistics() local oauth_token = token.fetch({ force = true }).oauth_token + local host = vim.env.GH_HOST or "github.com" + local endpoint + if host == "github.com" then + endpoint = "https://api.github.com/copilot_internal/v2/token" + else + -- GitHub Enterprise usually puts the API under /api/v3 + endpoint = string.format("https://%s/api/v3/copilot_internal/v2/token", host) + end + local ok, response = pcall(function() - return Curl.get("https://api.github.com/copilot_internal/user", { + return Curl.get(endpoint, { sync = true, headers = { Authorization = "Bearer " .. oauth_token, diff --git a/lua/codecompanion/adapters/http/copilot/token.lua b/lua/codecompanion/adapters/http/copilot/token.lua index 2f69b4334..3c7dcc95a 100644 --- a/lua/codecompanion/adapters/http/copilot/token.lua +++ b/lua/codecompanion/adapters/http/copilot/token.lua @@ -104,7 +104,7 @@ local function get_oauth_token() userdata = vim.json.decode(userdata) for key, value in pairs(userdata) do - if string.find(key, "github.com") then + if string.find(key, vim.env.GH_HOST or "github.com") then return value.oauth_token end end @@ -136,8 +136,17 @@ local function get_copilot_token() _token_fetch_in_progress = true log:trace("Authorizing GitHub Copilot token") + local host = vim.env.GH_HOST or "github.com" + local endpoint + if host == "github.com" then + endpoint = "https://api.github.com/copilot_internal/v2/token" + else + -- GitHub Enterprise usually puts the API under /api/v3 + endpoint = string.format("https://%s/api/v3/copilot_internal/v2/token", host) + end + local ok, request = pcall(function() - return Curl.get("https://api.github.com/copilot_internal/v2/token", { + return Curl.get(endpoint, { headers = { Authorization = "Bearer " .. (M._oauth_token or ""), Accept = "application/json",