mirror of
https://github.com/anomalyco/opencode.git
synced 2026-05-18 10:07:58 +00:00
151 lines
3.7 KiB
TypeScript
151 lines
3.7 KiB
TypeScript
import { z } from "zod"
|
|
import { Auth } from "./index"
|
|
import { NamedError } from "../util/error"
|
|
|
|
export namespace AuthGithubCopilot {
|
|
const CLIENT_ID = "Iv1.b507a08c87ecfe98"
|
|
const DEVICE_CODE_URL = "https://github.com/login/device/code"
|
|
const ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token"
|
|
const COPILOT_API_KEY_URL = "https://api.github.com/copilot_internal/v2/token"
|
|
|
|
interface DeviceCodeResponse {
|
|
device_code: string
|
|
user_code: string
|
|
verification_uri: string
|
|
expires_in: number
|
|
interval: number
|
|
}
|
|
|
|
interface AccessTokenResponse {
|
|
access_token?: string
|
|
error?: string
|
|
error_description?: string
|
|
}
|
|
|
|
interface CopilotTokenResponse {
|
|
token: string
|
|
expires_at: number
|
|
refresh_in: number
|
|
endpoints: {
|
|
api: string
|
|
}
|
|
}
|
|
|
|
export async function authorize() {
|
|
const deviceResponse = await fetch(DEVICE_CODE_URL, {
|
|
method: "POST",
|
|
headers: {
|
|
Accept: "application/json",
|
|
"Content-Type": "application/json",
|
|
"User-Agent": "GithubCopilot/1.155.0",
|
|
},
|
|
body: JSON.stringify({
|
|
client_id: CLIENT_ID,
|
|
scope: "read:user",
|
|
}),
|
|
})
|
|
const deviceData: DeviceCodeResponse = await deviceResponse.json()
|
|
return {
|
|
device: deviceData.device_code,
|
|
user: deviceData.user_code,
|
|
verification: deviceData.verification_uri,
|
|
interval: deviceData.interval || 5,
|
|
expiry: deviceData.expires_in,
|
|
}
|
|
}
|
|
|
|
export async function poll(device_code: string) {
|
|
const response = await fetch(ACCESS_TOKEN_URL, {
|
|
method: "POST",
|
|
headers: {
|
|
Accept: "application/json",
|
|
"Content-Type": "application/json",
|
|
"User-Agent": "GithubCopilot/1.155.0",
|
|
},
|
|
body: JSON.stringify({
|
|
client_id: CLIENT_ID,
|
|
device_code,
|
|
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
}),
|
|
})
|
|
|
|
if (!response.ok) return "failed"
|
|
|
|
const data: AccessTokenResponse = await response.json()
|
|
|
|
if (data.access_token) {
|
|
// Store the GitHub OAuth token
|
|
await Auth.set("github-copilot", {
|
|
type: "oauth",
|
|
refresh: data.access_token,
|
|
access: "",
|
|
expires: 0,
|
|
})
|
|
return "complete"
|
|
}
|
|
|
|
if (data.error === "authorization_pending") return "pending"
|
|
|
|
if (data.error) return "failed"
|
|
|
|
return "pending"
|
|
}
|
|
|
|
export async function access() {
|
|
const info = await Auth.get("github-copilot")
|
|
if (!info || info.type !== "oauth") return
|
|
if (info.access && info.expires > Date.now()) return info.access
|
|
|
|
// Get new Copilot API token
|
|
const response = await fetch(COPILOT_API_KEY_URL, {
|
|
headers: {
|
|
Accept: "application/json",
|
|
Authorization: `Bearer ${info.refresh}`,
|
|
"User-Agent": "GithubCopilot/1.155.0",
|
|
"Editor-Version": "vscode/1.85.1",
|
|
"Editor-Plugin-Version": "copilot/1.155.0",
|
|
},
|
|
})
|
|
|
|
if (!response.ok) return
|
|
|
|
const tokenData: CopilotTokenResponse = await response.json()
|
|
|
|
// Store the Copilot API token
|
|
await Auth.set("github-copilot", {
|
|
type: "oauth",
|
|
refresh: info.refresh,
|
|
access: tokenData.token,
|
|
expires: tokenData.expires_at * 1000,
|
|
})
|
|
|
|
return tokenData.token
|
|
}
|
|
|
|
export const DeviceCodeError = NamedError.create(
|
|
"DeviceCodeError",
|
|
z.object({}),
|
|
)
|
|
|
|
export const TokenExchangeError = NamedError.create(
|
|
"TokenExchangeError",
|
|
z.object({
|
|
message: z.string(),
|
|
}),
|
|
)
|
|
|
|
export const AuthenticationError = NamedError.create(
|
|
"AuthenticationError",
|
|
z.object({
|
|
message: z.string(),
|
|
}),
|
|
)
|
|
|
|
export const CopilotTokenError = NamedError.create(
|
|
"CopilotTokenError",
|
|
z.object({
|
|
message: z.string(),
|
|
}),
|
|
)
|
|
}
|