fix(core): discover Copilot API endpoint (#38184)

This commit is contained in:
Aiden Cline
2026-07-21 19:28:31 -05:00
committed by GitHub
parent da35eac93f
commit a0a6963beb
2 changed files with 57 additions and 13 deletions
@@ -13,6 +13,7 @@ import type { PluginInternal } from "../internal"
const clientID = "Ov23li8tweQw6odWQebz"
const apiVersion = "2026-06-01"
const userApiVersion = "2025-04-01"
const pollingSafetyMargin = 3000
const methodID = Integration.MethodID.make("device")
@@ -27,6 +28,14 @@ const Token = Schema.Struct({
error: Schema.optional(Schema.String),
interval: Schema.optional(Schema.Number),
})
const User = Schema.Struct({
endpoints: Schema.optional(
Schema.Struct({
api: Schema.optional(Schema.String),
}),
),
})
const decodeUser = Schema.decodeUnknownOption(User)
const JsonBody = Schema.UnknownFromJsonString
const decodeBody = Schema.decodeUnknownOption(JsonBody)
@@ -81,15 +90,35 @@ const oauth = (app: App.Info) => ({
Effect.map(Schema.decodeUnknownSync(Token)),
Effect.flatMap((token) => {
if (token.access_token) {
return Effect.succeed(
Credential.OAuth.make({
type: "oauth",
methodID,
refresh: token.access_token,
access: token.access_token,
expires: 0,
...(enterprise ? { metadata: { enterpriseUrl: domain } } : {}),
}),
const access = token.access_token
return request(
`${domain === "github.com" ? "https://api.github.com" : `https://api.${domain}`}/copilot_internal/user`,
{
headers: {
Accept: "application/json",
Authorization: `Bearer ${access}`,
"User-Agent": App.useragent(app),
"X-GitHub-Api-Version": userApiVersion,
},
},
).pipe(
Effect.map((user) => Option.getOrUndefined(decodeUser(user))?.endpoints?.api?.replace(/\/+$/, "")),
Effect.catch(() => Effect.succeed(undefined)),
Effect.map((apiEndpoint) =>
Credential.OAuth.make({
type: "oauth",
methodID,
refresh: access,
access,
expires: 0,
...((enterprise || apiEndpoint) && {
metadata: {
...(enterprise ? { enterpriseUrl: domain } : {}),
...(apiEndpoint ? { apiEndpoint } : {}),
},
}),
}),
),
)
}
if (token.error === "authorization_pending")
@@ -141,8 +170,7 @@ export const GithubCopilotPlugin = define({
return
}
const enterprise = credential.metadata?.enterpriseUrl
loaded.baseURL = baseURL(typeof enterprise === "string" ? enterprise : undefined)
loaded.baseURL = copilotBaseURL(credential.metadata)
const provider = yield* catalog.provider.get(ProviderV2.ID.githubCopilot)
const existing = (yield* catalog.model.all()).filter((model) => model.providerID === ProviderV2.ID.githubCopilot)
loaded.models = yield* Effect.tryPromise({
@@ -262,6 +290,13 @@ function baseURL(enterprise?: string) {
return enterprise ? `https://copilot-api.${normalizeDomain(enterprise)}` : "https://api.githubcopilot.com"
}
export function copilotBaseURL(metadata?: Readonly<Record<string, unknown>>) {
const endpoint = metadata?.apiEndpoint
if (typeof endpoint === "string" && endpoint) return endpoint
const enterprise = metadata?.enterpriseUrl
return baseURL(typeof enterprise === "string" ? enterprise : undefined)
}
function headers(app: App.Info) {
return {
Accept: "application/json",
@@ -1,12 +1,12 @@
import { AISDK } from "@opencode-ai/core/aisdk"
import { App } from "@opencode-ai/core/app"
import { describe, expect } from "bun:test"
import { describe, expect, test } from "bun:test"
import { Effect } from "effect"
import { Catalog } from "@opencode-ai/core/catalog"
import { ModelV2 } from "@opencode-ai/core/model"
import { PluginV2 } from "@opencode-ai/core/plugin"
import { PluginHost } from "@opencode-ai/core/plugin/host"
import { copilotFetch, GithubCopilotPlugin } from "@opencode-ai/core/plugin/provider/github-copilot"
import { copilotBaseURL, copilotFetch, GithubCopilotPlugin } from "@opencode-ai/core/plugin/provider/github-copilot"
import { ProviderV2 } from "@opencode-ai/core/provider"
import { Integration } from "@opencode-ai/core/integration"
import type { LanguageModelV3 } from "@ai-sdk/provider"
@@ -41,6 +41,15 @@ function fakeSelectorSdk(calls: string[]) {
}
describe("GithubCopilotPlugin", () => {
test("prefers the account-specific Copilot API endpoint", () => {
expect(
copilotBaseURL({
enterpriseUrl: "company.ghe.com",
apiEndpoint: "https://api.business.githubcopilot.com",
}),
).toBe("https://api.business.githubcopilot.com")
})
it.effect("registers GitHub Copilot device OAuth", () =>
Effect.gen(function* () {
yield* addPlugin()