186186(cl-defstruct (gptel--gh (:include gptel-openai)
187187 (:copier nil)
188188 (:constructor gptel--make-gh))
189- token github-token sessionid machineid)
190-
191- (defcustom gptel-gh-github-token-file (expand-file-name ".cache/copilot-chat/github-token"
192- user-emacs-directory)
193- "File where the GitHub token is stored."
194- :type 'string
195- :group 'gptel)
196-
197- (defcustom gptel-gh-token-file (expand-file-name ".cache/copilot-chat/token"
198- user-emacs-directory)
199- "File where the chat token is cached."
200- :type 'string
201- :group 'gptel)
189+ sessionid machineid)
202190
203191(defconst gptel--gh-auth-common-headers
204192 `(("editor-plugin-version" . "gptel/*")
205193 ("editor-version" . ,(concat "emacs/" emacs-version))))
206194
207195(defconst gptel--gh-client-id "Iv1.b507a08c87ecfe98")
208196
197+ (defvar gptel--gh-token nil)
198+ (defvar gptel--gh-github-token nil)
199+
209200;; https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random)
210201(defun gptel--gh-uuid ()
211202 "Generate a UUID v4-1."
225216 (setq hex (nconc hex (list (aref hex-chars (random 16))))))
226217 (apply #'string hex)))
227218
228- (defun gptel--gh-restore (file)
229- "Restore saved object from FILE."
230- (when (file-exists-p file)
231- ;; We set the coding system to `utf-8-auto-dos' when reading so that
232- ;; files with CR EOL can still be read properly
233- (let ((coding-system-for-read 'utf-8-auto-dos))
234- (with-temp-buffer
235- (set-buffer-multibyte nil)
236- (insert-file-contents-literally file)
237- (goto-char (point-min))
238- (read (current-buffer))))))
239-
240- (defun gptel--gh-save (file obj)
219+ (defun gptel--gh-save (obj)
241220 "Save OBJ to FILE."
242- (let ((print-length nil)
243- (print-level nil)
244- (coding-system-for-write 'utf-8-unix))
245- (make-directory (file-name-directory file) t)
246- (write-region (prin1-to-string obj) nil file nil :silent)
247- obj))
221+ (message "New GitHub token: %s" (prin1-to-string obj))
222+ (setq gptel--gh-github-token obj))
248223
249224(defun gptel-gh-login ()
250225 "Login to GitHub Copilot API.
@@ -276,13 +251,11 @@ If your browser does not open automatically, browse to %s."
276251 :device_code ,device_code
277252 :grant_type "urn:ietf:params:oauth:grant-type:device_code"))
278253 :access_token)
279- (gptel--gh-save gptel-gh-github-token-file)
280- (setf (gptel--gh-github-token gptel-backend))))
281- (if (and (gptel--gh-github-token gptel-backend)
282- (not (string-empty-p
283- (gptel--gh-github-token gptel-backend))))
284- (message "Successfully logged in to GitHub Copilot")
285- (user-error "Error: You might not have access to GitHub Copilot Chat!")))
254+ (gptel--gh-save))
255+ (if (and gptel--gh-github-token
256+ (not (string-empty-p gptel--gh-github-token)))
257+ (message "Successfully logged in to GitHub Copilot")
258+ (user-error "Error: You might not have access to GitHub Copilot Chat!"))))
286259
287260(defun gptel--gh-renew-token ()
288261 "Renew session token."
@@ -291,34 +264,22 @@ If your browser does not open automatically, browse to %s."
291264 "https://api.github.com/copilot_internal/v2/token"
292265 :method 'get
293266 :headers `(("authorization"
294- . ,(format "token %s" ( gptel--gh-github-token gptel-backend) ))
267+ . ,(format "token %s" gptel--gh-github-token))
295268 ,@gptel--gh-auth-common-headers))))
296269 (if (not (plist-get token :token))
297- (progn
298- (setf (gptel--gh-github-token gptel-backend) nil)
299- (user-error "Error: You might not have access to GitHub Copilot Chat!"))
300- (thread-last
301- (gptel--gh-save gptel-gh-token-file token)
302- (setf (gptel--gh-token gptel-backend))))))
270+ (user-error "Error: You might not have access to GitHub Copilot Chat!")
271+ (setq gptel--gh-token token))))
303272
304273(defun gptel--gh-auth ()
305274 "Authenticate with GitHub Copilot API.
306275
307276We first need github authorization (github token).
308277Then we need a session token."
309- (unless (gptel--gh-github-token gptel-backend)
310- (let ((token (gptel--gh-restore gptel-gh-github-token-file)))
311- (if token
312- (setf (gptel--gh-github-token gptel-backend) token)
313- (gptel-gh-login))))
314-
315- (when (null (gptel--gh-token gptel-backend))
316- ;; try to load token from `gptel-gh-token-file'
317- (setf (gptel--gh-token gptel-backend)
318- (gptel--gh-restore gptel-gh-token-file)))
278+ (unless gptel--gh-github-token
279+ (gptel-gh-login))
319280
320281 (pcase-let (((map :token :expires_at)
321- ( gptel--gh-token gptel-backend) ))
282+ gptel--gh-token))
322283 (when (or (null token)
323284 (and expires_at
324285 (> (round (float-time (current-time)))
@@ -331,8 +292,7 @@ Then we need a session token."
331292 (header (lambda ()
332293 (gptel--gh-auth)
333294 `(("openai-intent" . "conversation-panel")
334- ("authorization" . ,(concat "Bearer "
335- (plist-get (gptel--gh-token gptel-backend) :token)))
295+ ("authorization" . ,(concat "Bearer " (plist-get gptel--gh-token :token)))
336296 ("x-request-id" . ,(gptel--gh-uuid))
337297 ("vscode-sessionid" . ,(or (gptel--gh-sessionid gptel-backend) ""))
338298 ("vscode-machineid" . ,(or (gptel--gh-machineid gptel-backend) ""))
@@ -344,7 +304,8 @@ Then we need a session token."
344304 (protocol "https")
345305 (endpoint "/chat/completions")
346306 (stream t)
347- (models gptel--gh-models))
307+ (models gptel--gh-models)
308+ (github-token-init nil))
348309 "Register a Github Copilot chat backend for gptel with NAME.
349310
350311Keyword arguments:
@@ -396,6 +357,9 @@ parameters (as plist keys) and values supported by the API. Use
396357these to set parameters that gptel does not provide user options
397358for."
398359 (declare (indent 1))
360+ (if (and (not gptel--gh-github-token)
361+ github-token-init)
362+ (gptel--gh-save (funcall github-token-init)))
399363 (let ((backend (gptel--make-gh
400364 :name name
401365 :host host
0 commit comments