164164(cl-defstruct (gptel--gh (:include gptel-openai)
165165 (:copier nil )
166166 (:constructor gptel--make-gh))
167- token github-token sessionid machineid)
168-
169- (defcustom gptel-gh-github-token-file (expand-file-name " .cache/copilot-chat/github-token"
170- user-emacs-directory)
171- " File where the GitHub token is stored."
172- :type 'string
173- :group 'gptel )
174-
175- (defcustom gptel-gh-token-file (expand-file-name " .cache/copilot-chat/token"
176- user-emacs-directory)
177- " File where the chat token is cached."
178- :type 'string
179- :group 'gptel )
167+ sessionid machineid)
180168
181169(defconst gptel--gh-auth-common-headers
182170 `((" editor-plugin-version" . " gptel/*" )
183171 (" editor-version" . ,(concat " emacs/" emacs-version))))
184172
185173(defconst gptel--gh-client-id " Iv1.b507a08c87ecfe98" )
186174
175+ (defvar gptel--gh-token nil )
176+ (defvar gptel--gh-github-token nil )
177+
187178; ; https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random)
188179(defun gptel--gh-uuid ()
189180 " Generate a UUID v4-1."
203194 (setq hex (nconc hex (list (aref hex-chars (random 16 ))))))
204195 (apply #'string hex)))
205196
206- (defun gptel--gh-restore (file )
207- " Restore saved object from FILE."
208- (when (file-exists-p file)
209- ; ; We set the coding system to `utf-8-auto-dos' when reading so that
210- ; ; files with CR EOL can still be read properly
211- (let ((coding-system-for-read 'utf-8-auto-dos ))
212- (with-temp-buffer
213- (set-buffer-multibyte nil )
214- (insert-file-contents-literally file)
215- (goto-char (point-min ))
216- (read (current-buffer ))))))
217-
218- (defun gptel--gh-save (file obj )
197+ (defun gptel--gh-save (obj )
219198 " Save OBJ to FILE."
220- (let ((print-length nil )
221- (print-level nil )
222- (coding-system-for-write 'utf-8-unix ))
223- (make-directory (file-name-directory file) t )
224- (write-region (prin1-to-string obj) nil file nil :silent )
225- obj))
199+ (message " New GitHub token: %s " (prin1-to-string obj))
200+ (setq gptel--gh-github-token obj))
226201
227202(defun gptel-gh-login ()
228203 " Login to GitHub Copilot API.
@@ -254,13 +229,11 @@ If your browser does not open automatically, browse to %s."
254229 :device_code , device_code
255230 :grant_type " urn:ietf:params:oauth:grant-type:device_code" ))
256231 :access_token )
257- (gptel--gh-save gptel-gh-github-token-file)
258- (setf (gptel--gh-github-token gptel-backend))))
259- (if (and (gptel--gh-github-token gptel-backend)
260- (not (string-empty-p
261- (gptel--gh-github-token gptel-backend))))
262- (message " Successfully logged in to GitHub Copilot " )
263- (user-error " Error: You might not have access to GitHub Copilot Chat!" )))
232+ (gptel--gh-save))
233+ (if (and gptel--gh-github-token
234+ (not (string-empty-p gptel--gh-github-token)))
235+ (message " Successfully logged in to GitHub Copilot " )
236+ (user-error " Error: You might not have access to GitHub Copilot Chat!" ))))
264237
265238(defun gptel--gh-renew-token ()
266239 " Renew session token."
@@ -269,34 +242,22 @@ If your browser does not open automatically, browse to %s."
269242 " https://api.github.com/copilot_internal/v2/token"
270243 :method 'get
271244 :headers `((" authorization"
272- . ,(format " token %s " ( gptel--gh-github-token gptel-backend) ))
245+ . ,(format " token %s " gptel--gh-github-token))
273246 ,@gptel--gh-auth-common-headers ))))
274247 (if (not (plist-get token :token ))
275- (progn
276- (setf (gptel--gh-github-token gptel-backend) nil )
277- (user-error " Error: You might not have access to GitHub Copilot Chat!" ))
278- (thread-last
279- (gptel--gh-save gptel-gh-token-file token)
280- (setf (gptel--gh-token gptel-backend))))))
248+ (user-error " Error: You might not have access to GitHub Copilot Chat!" )
249+ (setq gptel--gh-token token))))
281250
282251(defun gptel--gh-auth ()
283252 " Authenticate with GitHub Copilot API.
284253
285254We first need github authorization (github token).
286255Then we need a session token."
287- (unless (gptel--gh-github-token gptel-backend)
288- (let ((token (gptel--gh-restore gptel-gh-github-token-file)))
289- (if token
290- (setf (gptel--gh-github-token gptel-backend) token)
291- (gptel-gh-login))))
292-
293- (when (null (gptel--gh-token gptel-backend))
294- ; ; try to load token from `gptel-gh-token-file'
295- (setf (gptel--gh-token gptel-backend)
296- (gptel--gh-restore gptel-gh-token-file)))
256+ (unless gptel--gh-github-token
257+ (gptel-gh-login))
297258
298259 (pcase-let (((map :token :expires_at )
299- ( gptel--gh-token gptel-backend) ))
260+ gptel--gh-token))
300261 (when (or (null token)
301262 (and expires_at
302263 (> (round (float-time (current-time )))
@@ -309,8 +270,7 @@ Then we need a session token."
309270 (header (lambda ()
310271 (gptel--gh-auth)
311272 `((" openai-intent" . " conversation-panel" )
312- (" authorization" . ,(concat " Bearer "
313- (plist-get (gptel--gh-token gptel-backend) :token )))
273+ (" authorization" . ,(concat " Bearer " (plist-get gptel--gh-token :token )))
314274 (" x-request-id" . ,(gptel--gh-uuid))
315275 (" vscode-sessionid" . ,(or (gptel--gh-sessionid gptel-backend) " " ))
316276 (" vscode-machineid" . ,(or (gptel--gh-machineid gptel-backend) " " ))
@@ -322,7 +282,8 @@ Then we need a session token."
322282 (protocol " https" )
323283 (endpoint " /chat/completions" )
324284 (stream t )
325- (models gptel--gh-models))
285+ (models gptel--gh-models)
286+ (github-token-init nil ))
326287 " Register a Github Copilot chat backend for gptel with NAME.
327288
328289Keyword arguments:
@@ -374,6 +335,9 @@ parameters (as plist keys) and values supported by the API. Use
374335these to set parameters that gptel does not provide user options
375336for."
376337 (declare (indent 1 ))
338+ (if (and (not gptel--gh-github-token)
339+ github-token-init)
340+ (gptel--gh-save (funcall github-token-init)))
377341 (let ((backend (gptel--make-gh
378342 :name name
379343 :host host
0 commit comments