feat(ai): adapt OAuth flows to OAuthAuth (phase 4)

anthropic, openai-codex, and github-copilot flow modules gain OAuthAuth
exports (login/refresh/toAuth) wired to the prompt()/notify() login
callbacks, making the lazyOAuth attachments on the provider factories
functional. Copilot's modifyModels baseUrl rewriting becomes toAuth()
returning ModelAuth.baseUrl derived from the token proxy endpoint.

Callback-server flows race a manual_code prompt and abort it through
AuthPrompt.signal once the flow settles; OAuthAuth has no
usesCallbackServer flag. The old OAuthProviderInterface exports stay
unchanged until the coding-agent migration.
This commit is contained in:
Mario Zechner
2026-06-10 20:41:29 +02:00
parent fec0c3d12f
commit 4d5c015820
5 changed files with 293 additions and 2 deletions
@@ -2,6 +2,7 @@
* GitHub Copilot OAuth flow
*/
import type { OAuthAuth, OAuthCredential } from "../../auth/types.ts";
import { getModels } from "../../models.ts";
import type { Api, Model } from "../../types.ts";
import { pollOAuthDeviceCodeFlow } from "./device-code.ts";
@@ -330,6 +331,42 @@ export async function loginGitHubCopilot(options: {
return credentials;
}
function copilotEnterpriseDomain(credential: OAuthCredential): string | undefined {
const enterpriseUrl = credential.enterpriseUrl;
if (typeof enterpriseUrl !== "string" || !enterpriseUrl) return undefined;
return normalizeDomain(enterpriseUrl) ?? undefined;
}
export const githubCopilotOAuth: OAuthAuth = {
name: "GitHub Copilot",
async login(callbacks) {
const credentials = await loginGitHubCopilot({
onDeviceCode: (info) => callbacks.notify({ type: "device_code", ...info }),
onPrompt: (prompt) =>
callbacks.prompt({ type: "text", message: prompt.message, placeholder: prompt.placeholder }),
onProgress: (message) => callbacks.notify({ type: "progress", message }),
signal: callbacks.signal,
});
return { ...credentials, type: "oauth" };
},
async refresh(credential) {
return {
...(await refreshGitHubCopilotToken(credential.refresh, copilotEnterpriseDomain(credential))),
type: "oauth",
};
},
/** Per-credential baseUrl from the token's proxy endpoint replaces the old `modifyModels` rewriting. */
async toAuth(credential) {
return {
apiKey: credential.access,
baseUrl: getGitHubCopilotBaseUrl(credential.access, copilotEnterpriseDomain(credential)),
};
},
};
export const githubCopilotOAuthProvider: OAuthProviderInterface = {
id: "github-copilot",
name: "GitHub Copilot",