Skip to content

Commit e7e9daf

Browse files
committed
refactor: extract token retrieval into smaller functions
1 parent 2d925b1 commit e7e9daf

4 files changed

Lines changed: 94 additions & 63 deletions

File tree

src/lib/paths.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
import fs from "node:fs/promises"
12
import os from "node:os"
2-
import path from "pathe"
3+
import path from "node:path"
34

45
const APP_DIR = path.join(os.homedir(), ".local", "share", "copilot-api")
56

@@ -9,3 +10,17 @@ export const PATHS = {
910
APP_DIR,
1011
GITHUB_TOKEN_PATH,
1112
}
13+
14+
export async function ensurePaths(): Promise<void> {
15+
await fs.mkdir(PATHS.APP_DIR, { recursive: true })
16+
await ensureFile(PATHS.GITHUB_TOKEN_PATH)
17+
}
18+
19+
async function ensureFile(filePath: string): Promise<void> {
20+
try {
21+
await fs.access(filePath, fs.constants.W_OK)
22+
} catch {
23+
await fs.writeFile(filePath, "")
24+
await fs.chmod(filePath, 0o600)
25+
}
26+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { GITHUB_BASE_URL, GITHUB_CLIENT_ID } from "~/lib/constants"
2+
3+
export async function getDeviceCode(): Promise<DeviceCodeResponse> {
4+
const response = await fetch(`${GITHUB_BASE_URL}/login/device/code`, {
5+
method: "POST",
6+
body: JSON.stringify({
7+
client_id: GITHUB_CLIENT_ID,
8+
}),
9+
})
10+
11+
if (!response.ok) {
12+
throw new Error("Failed to get device code", {
13+
cause: await response.json(),
14+
})
15+
}
16+
17+
return (await response.json()) as DeviceCodeResponse
18+
}
19+
20+
export interface DeviceCodeResponse {
21+
device_code: string
22+
user_code: string
23+
verification_uri: string
24+
expires_in: number
25+
interval: number
26+
}
Lines changed: 7 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,14 @@
1-
import { sleep } from "bun"
21
import consola from "consola"
32

4-
import { GITHUB_CLIENT_ID, GITHUB_BASE_URL } from "~/lib/constants"
5-
6-
interface DeviceCodeResponse {
7-
device_code: string
8-
user_code: string
9-
verification_uri: string
10-
expires_in: number
11-
interval: number
12-
}
13-
14-
interface AccessTokenResponse {
15-
access_token: string
16-
token_type: string
17-
scope: string
18-
}
3+
import { getDeviceCode } from "../get-device-code"
4+
import { pollAccessToken } from "../poll-access-token"
195

206
export async function getGitHubToken() {
21-
const response = await fetch(`${GITHUB_BASE_URL}/login/device/code`, {
22-
method: "POST",
23-
body: JSON.stringify({
24-
client_id: GITHUB_CLIENT_ID,
25-
}),
26-
})
27-
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}`)
38-
39-
while (true) {
40-
const response = await fetch(
41-
`${GITHUB_BASE_URL}/login/oauth/access_token`,
42-
{
43-
method: "POST",
44-
body: JSON.stringify({
45-
client_id: GITHUB_CLIENT_ID,
46-
device_code,
47-
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
48-
}),
49-
},
50-
)
51-
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-
}
7+
const response = await getDeviceCode()
608

61-
const { access_token } = (await response.json()) as AccessTokenResponse
9+
consola.info(
10+
`Please enter the code "${response.user_code}" in ${response.verification_uri}`,
11+
)
6212

63-
if (access_token) {
64-
return access_token
65-
} else {
66-
await sleep(sleepDuration)
67-
}
68-
}
13+
return await pollAccessToken(response)
6914
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { GITHUB_BASE_URL, GITHUB_CLIENT_ID } from "~/lib/constants"
2+
import { sleep } from "~/lib/sleep"
3+
4+
import type { DeviceCodeResponse } from "./get-device-code"
5+
6+
export async function pollAccessToken(
7+
deviceCode: DeviceCodeResponse,
8+
): Promise<string> {
9+
while (true) {
10+
const response = await fetch(
11+
`${GITHUB_BASE_URL}/login/oauth/access_token`,
12+
{
13+
method: "POST",
14+
body: JSON.stringify({
15+
client_id: GITHUB_CLIENT_ID,
16+
device_code: deviceCode.device_code,
17+
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
18+
}),
19+
},
20+
)
21+
22+
// Interval is in seconds, we need to multiply by 1000 to get milliseconds
23+
// I'm also adding another second, just to be safe
24+
const sleepDuration = (deviceCode.interval + 1) * 1000
25+
26+
if (!response.ok) {
27+
await sleep(sleepDuration)
28+
continue
29+
}
30+
31+
const { access_token } = (await response.json()) as AccessTokenResponse
32+
33+
if (access_token) {
34+
return access_token
35+
} else {
36+
await sleep(sleepDuration)
37+
}
38+
}
39+
}
40+
41+
interface AccessTokenResponse {
42+
access_token: string
43+
token_type: string
44+
scope: string
45+
}

0 commit comments

Comments
 (0)