|
1 | | -import { execa } from "execa" |
| 1 | +import consola from "consola" |
2 | 2 |
|
3 | | -// @ts-expect-error TypeScript can't analyze timeout |
4 | | -export async function getGitHubToken(): Promise<string> { |
5 | | - // Kill any existing vscode processes |
6 | | - // otherwise, no token call will be made |
7 | | - await killVSCodeProcesses() |
8 | | - |
9 | | - const mitmdump = createMitmdumpProcess() |
10 | | - void createVSCodeProcess() |
11 | | - |
12 | | - const timeout = setTimeout(() => { |
13 | | - throw new Error("Timed out waiting for token") |
14 | | - }, 30_000) |
| 3 | +import { ENV } from "~/lib/env" |
| 4 | +import { _github } from "~/services/api-instance" |
15 | 5 |
|
16 | | - for await (const line of mitmdump.stdout) { |
17 | | - if (typeof line !== "string") continue |
18 | | - if (!line.includes("authorization: token")) continue |
19 | | - |
20 | | - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion |
21 | | - const token = line |
22 | | - .split("\n") |
23 | | - .map((line) => line.trim()) |
24 | | - .filter(Boolean) |
25 | | - .find((line) => line.includes("authorization: token"))! |
26 | | - .split("authorization: token ") |
27 | | - .at(1)! |
28 | | - .trim() |
| 6 | +interface DeviceCodeResponse { |
| 7 | + device_code: string |
| 8 | + user_code: string |
| 9 | + verification_uri: string |
| 10 | + expires_in: number |
| 11 | + interval: number |
| 12 | +} |
29 | 13 |
|
30 | | - clearTimeout(timeout) |
| 14 | +interface AccessTokenResponse { |
| 15 | + access_token: string |
| 16 | + token_type: string |
| 17 | + scope: string |
| 18 | +} |
31 | 19 |
|
32 | | - await killVSCodeProcesses() |
33 | | - mitmdump.kill() |
| 20 | +export async function getGitHubToken(): Promise<string> { |
| 21 | + consola.start("Getting GitHub device code") |
34 | 22 |
|
35 | | - return token |
| 23 | + const response = await _github<DeviceCodeResponse>("/login/device/code", { |
| 24 | + method: "POST", |
| 25 | + body: { |
| 26 | + client_id: ENV.GITHUB_CLIENT_ID, |
| 27 | + scope: ENV.GITHUB_OAUTH_SCOPES, |
| 28 | + }, |
| 29 | + }) |
| 30 | + |
| 31 | + consola.info( |
| 32 | + `Please enter the code "${response.user_code}" in ${response.verification_uri}`, |
| 33 | + ) |
| 34 | + |
| 35 | + while (true) { |
| 36 | + const pollResponse = await _github<AccessTokenResponse>( |
| 37 | + "/login/oauth/access_token", |
| 38 | + { |
| 39 | + method: "POST", |
| 40 | + body: { |
| 41 | + client_id: ENV.GITHUB_CLIENT_ID, |
| 42 | + device_code: response.device_code, |
| 43 | + grant_type: "urn:ietf:params:oauth:grant-type:device_code", |
| 44 | + }, |
| 45 | + }, |
| 46 | + ) |
| 47 | + |
| 48 | + if (pollResponse.access_token) { |
| 49 | + consola.info(`Got token ${pollResponse.access_token.replace(/./g, "*")}`) |
| 50 | + return pollResponse.access_token |
| 51 | + } 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 | + ) |
| 57 | + } |
36 | 58 | } |
37 | 59 | } |
38 | | - |
39 | | -const createMitmdumpProcess = () => |
40 | | - execa({ reject: false })("mitmdump", [ |
41 | | - "--flow-detail", |
42 | | - "4", |
43 | | - "~m GET & ~u https://api.github.com/copilot_internal/v2/token", |
44 | | - ]) |
45 | | - |
46 | | -const createVSCodeProcess = () => |
47 | | - execa({ |
48 | | - reject: false, |
49 | | - env: { |
50 | | - http_proxy: "http://127.0.0.1:8080", |
51 | | - https_proxy: "http://127.0.0.1:8080", |
52 | | - }, |
53 | | - })("code", [ |
54 | | - "--ignore-certificate-errors", |
55 | | - // Can be whatever folder, as long as it's trusted by vscode |
56 | | - // https://code.visualstudio.com/docs/editor/workspace-trust |
57 | | - "/home/erick/Documents/sides/playground/", |
58 | | - ]) |
59 | | - |
60 | | -const killVSCodeProcesses = () => execa({ reject: false })("pkill", ["code"]) |
0 commit comments