@@ -10,8 +10,13 @@ import {
1010 OPENAI_ORGANIZATION,
1111 OPENAI_PROJECT,
1212} from "./config.js";
13+ import fs from "fs";
1314import OpenAI, { AzureOpenAI } from "openai";
1415import * as Errors from "openai/error";
16+ import os from "os";
17+ import path from "path";
18+
19+ const COPILOT_TOKEN_FILE = path.join(os.homedir(), ".codex", "copilot-token.json");
1520
1621type OpenAIClientConfig = {
1722 provider: string;
@@ -82,6 +87,22 @@ export class GithubCopilotClient extends OpenAI {
8287 ) {
8388 return this.copilotToken;
8489 }
90+ try {
91+ const disk = JSON.parse(fs.readFileSync(COPILOT_TOKEN_FILE, "utf-8"));
92+ if (
93+ typeof disk.token === "string" &&
94+ typeof disk.expiration === "string"
95+ ) {
96+ const exp = new Date(disk.expiration);
97+ if (exp.getTime() > Date.now()) {
98+ this.copilotToken = disk.token;
99+ this.copilotTokenExpiration = exp;
100+ return this.copilotToken;
101+ }
102+ }
103+ } catch {
104+ /* ignore */
105+ }
85106 const resp = await fetch(
86107 "https://api.github.com/copilot_internal/v2/token",
87108 {
@@ -106,6 +127,23 @@ export class GithubCopilotClient extends OpenAI {
106127 }
107128 this.copilotToken = token;
108129 this.copilotTokenExpiration = new Date(Date.now() + refresh_in * 1000);
130+ try {
131+ fs.mkdirSync(path.dirname(COPILOT_TOKEN_FILE), { recursive: true });
132+ fs.writeFileSync(
133+ COPILOT_TOKEN_FILE,
134+ JSON.stringify(
135+ {
136+ token: this.copilotToken,
137+ expiration: this.copilotTokenExpiration.toISOString(),
138+ },
139+ null,
140+ 2,
141+ ),
142+ { mode: 0o600 },
143+ );
144+ } catch {
145+ /* ignore */
146+ }
109147 return token;
110148 }
111149
0 commit comments