Skip to content

Commit 2d925b1

Browse files
committed
refactor: use native fetch for github device flow
1 parent 107c528 commit 2d925b1

File tree

5 files changed

+61
-30
lines changed

5 files changed

+61
-30
lines changed

src/lib/constants.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
// VSCode client ID
2-
const GITHUB_CLIENT_ID = "01ab8ac9400c4e429b23"
32
const GITHUB_OAUTH_SCOPES = [
43
"read:org",
54
"read:user",
@@ -9,7 +8,7 @@ const GITHUB_OAUTH_SCOPES = [
98
].join(" ")
109

1110
export const ENV = {
12-
GITHUB_CLIENT_ID,
11+
GITHUB_CLIENT_ID: "01ab8ac9400c4e429b23",
1312
GITHUB_OAUTH_SCOPES,
1413
}
1514

@@ -29,3 +28,13 @@ export const GITHUB_API_CONFIG = {
2928
export const GITHUB_WEB_API_CONFIG = {
3029
baseURL: "https://github.com",
3130
} as const
31+
32+
export const GITHUB_BASE_URL = "https://github.com"
33+
export const GITHUB_CLIENT_ID = "01ab8ac9400c4e429b23"
34+
export const GITHUB_APP_SCOPES = [
35+
"read:org",
36+
"read:user",
37+
"repo",
38+
"user:email",
39+
"workflow",
40+
].join(" ")

src/lib/initialization.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,7 @@ async function logModelInformation(): Promise<void> {
6262

6363
async function initializeGithubToken() {
6464
consola.start("Getting GitHub device code")
65-
const tokenResponse = await getGitHubToken()
66-
return tokenResponse.access_token
65+
return await getGitHubToken()
6766
}
6867

6968
async function logUser() {

src/lib/sleep.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export const sleep = (ms: number) =>
2+
new Promise((resolve) => {
3+
setTimeout(resolve, ms)
4+
})

src/lib/state.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import type { GetModelsResponse } from "~/services/copilot/get-models/types"
2+
3+
interface State {
4+
githubToken?: string
5+
copilotToken?: string
6+
models?: GetModelsResponse
7+
}
8+
9+
export const state: State = {}
Lines changed: 36 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1+
import { sleep } from "bun"
12
import consola from "consola"
23

3-
import { ENV } from "~/lib/constants"
4-
import { _github } from "~/services/api-instance"
4+
import { GITHUB_CLIENT_ID, GITHUB_BASE_URL } from "~/lib/constants"
55

66
interface DeviceCodeResponse {
77
device_code: string
@@ -18,42 +18,52 @@ interface AccessTokenResponse {
1818
}
1919

2020
export async function getGitHubToken() {
21-
const response = await _github<DeviceCodeResponse>("/login/device/code", {
21+
const response = await fetch(`${GITHUB_BASE_URL}/login/device/code`, {
2222
method: "POST",
23-
body: {
24-
client_id: ENV.GITHUB_CLIENT_ID,
25-
scope: ENV.GITHUB_OAUTH_SCOPES,
26-
},
23+
body: JSON.stringify({
24+
client_id: GITHUB_CLIENT_ID,
25+
}),
2726
})
2827

29-
consola.info(
30-
`Please enter the code "${response.user_code}" in ${response.verification_uri}`,
31-
)
28+
if (!response.ok) {
29+
throw new Error("Failed to get device code", {
30+
cause: await response.json(),
31+
})
32+
}
33+
34+
const { user_code, verification_uri, device_code, interval } =
35+
(await response.json()) as DeviceCodeResponse
36+
37+
consola.info(`Please enter the code "${user_code}" in ${verification_uri}`)
3238

3339
while (true) {
34-
const pollResponse = await _github<AccessTokenResponse>(
35-
"/login/oauth/access_token",
40+
const response = await fetch(
41+
`${GITHUB_BASE_URL}/login/oauth/access_token`,
3642
{
3743
method: "POST",
38-
body: {
39-
client_id: ENV.GITHUB_CLIENT_ID,
40-
device_code: response.device_code,
44+
body: JSON.stringify({
45+
client_id: GITHUB_CLIENT_ID,
46+
device_code,
4147
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
42-
},
48+
}),
4349
},
4450
)
4551

46-
if (pollResponse.access_token) {
47-
consola.info(
48-
`Got token ${pollResponse.access_token.replaceAll(/./g, "*")}`,
49-
)
50-
return pollResponse
52+
// Interval is in seconds, we need to multiply by 1000 to get milliseconds
53+
// I'm also adding another second, just to be safe
54+
const sleepDuration = (interval + 1) * 1000
55+
56+
if (!response.ok) {
57+
await sleep(sleepDuration)
58+
continue
59+
}
60+
61+
const { access_token } = (await response.json()) as AccessTokenResponse
62+
63+
if (access_token) {
64+
return access_token
5165
} else {
52-
// Interval is in seconds, we need to multiply by 1000 to get milliseconds
53-
// I'm also adding another second, just to be safe
54-
await new Promise((resolve) =>
55-
setTimeout(resolve, (response.interval + 1) * 1000),
56-
)
66+
await sleep(sleepDuration)
5767
}
5868
}
5969
}

0 commit comments

Comments
 (0)