170170(cl-defstruct (gptel--gh (:include gptel-openai)
171171 (:copier nil )
172172 (:constructor gptel--make-gh))
173- token github-token sessionid machineid)
174-
175- (defcustom gptel-gh-github-token-file (expand-file-name " .cache/copilot-chat/github-token"
176- user-emacs-directory)
177- " File where the GitHub token is stored."
178- :type 'string
179- :group 'gptel )
180-
181- (defcustom gptel-gh-token-file (expand-file-name " .cache/copilot-chat/token"
182- user-emacs-directory)
183- " File where the chat token is cached."
184- :type 'string
185- :group 'gptel )
173+ sessionid machineid)
186174
187175(defconst gptel--gh-auth-common-headers
188176 `((" editor-plugin-version" . " gptel/*" )
189177 (" editor-version" . ,(concat " emacs/" emacs-version))))
190178
191179(defconst gptel--gh-client-id " Iv1.b507a08c87ecfe98" )
192180
181+ (defvar gptel--gh-token nil )
182+ (defvar gptel--gh-github-token nil )
183+
193184; ; https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random)
194185(defun gptel--gh-uuid ()
195186 " Generate a UUID v4-1."
209200 (setq hex (nconc hex (list (aref hex-chars (random 16 ))))))
210201 (apply #'string hex)))
211202
212- (defun gptel--gh-restore (file )
213- " Restore saved object from FILE."
214- (when (file-exists-p file)
215- ; ; We set the coding system to `utf-8-auto-dos' when reading so that
216- ; ; files with CR EOL can still be read properly
217- (let ((coding-system-for-read 'utf-8-auto-dos ))
218- (with-temp-buffer
219- (set-buffer-multibyte nil )
220- (insert-file-contents-literally file)
221- (goto-char (point-min ))
222- (read (current-buffer ))))))
223-
224- (defun gptel--gh-save (file obj )
203+ (defun gptel--gh-save (obj )
225204 " Save OBJ to FILE."
226- (let ((print-length nil )
227- (print-level nil )
228- (coding-system-for-write 'utf-8-unix ))
229- (make-directory (file-name-directory file) t )
230- (write-region (prin1-to-string obj) nil file nil :silent )
231- obj))
205+ (message " New GitHub token: %s " (prin1-to-string obj))
206+ (setq gptel--gh-github-token obj))
232207
233208(defun gptel-gh-login ()
234209 " Login to GitHub Copilot API.
@@ -260,13 +235,11 @@ If your browser does not open automatically, browse to %s."
260235 :device_code , device_code
261236 :grant_type " urn:ietf:params:oauth:grant-type:device_code" ))
262237 :access_token )
263- (gptel--gh-save gptel-gh-github-token-file)
264- (setf (gptel--gh-github-token gptel-backend))))
265- (if (and (gptel--gh-github-token gptel-backend)
266- (not (string-empty-p
267- (gptel--gh-github-token gptel-backend))))
268- (message " Successfully logged in to GitHub Copilot " )
269- (user-error " Error: You might not have access to GitHub Copilot Chat!" )))
238+ (gptel--gh-save))
239+ (if (and gptel--gh-github-token
240+ (not (string-empty-p gptel--gh-github-token)))
241+ (message " Successfully logged in to GitHub Copilot " )
242+ (user-error " Error: You might not have access to GitHub Copilot Chat!" ))))
270243
271244(defun gptel--gh-renew-token ()
272245 " Renew session token."
@@ -275,34 +248,22 @@ If your browser does not open automatically, browse to %s."
275248 " https://api.github.com/copilot_internal/v2/token"
276249 :method 'get
277250 :headers `((" authorization"
278- . ,(format " token %s " ( gptel--gh-github-token gptel-backend) ))
251+ . ,(format " token %s " gptel--gh-github-token))
279252 ,@gptel--gh-auth-common-headers ))))
280253 (if (not (plist-get token :token ))
281- (progn
282- (setf (gptel--gh-github-token gptel-backend) nil )
283- (user-error " Error: You might not have access to GitHub Copilot Chat!" ))
284- (thread-last
285- (gptel--gh-save gptel-gh-token-file token)
286- (setf (gptel--gh-token gptel-backend))))))
254+ (user-error " Error: You might not have access to GitHub Copilot Chat!" )
255+ (setq gptel--gh-token token))))
287256
288257(defun gptel--gh-auth ()
289258 " Authenticate with GitHub Copilot API.
290259
291260We first need github authorization (github token).
292261Then we need a session token."
293- (unless (gptel--gh-github-token gptel-backend)
294- (let ((token (gptel--gh-restore gptel-gh-github-token-file)))
295- (if token
296- (setf (gptel--gh-github-token gptel-backend) token)
297- (gptel-gh-login))))
298-
299- (when (null (gptel--gh-token gptel-backend))
300- ; ; try to load token from `gptel-gh-token-file'
301- (setf (gptel--gh-token gptel-backend)
302- (gptel--gh-restore gptel-gh-token-file)))
262+ (unless gptel--gh-github-token
263+ (gptel-gh-login))
303264
304265 (pcase-let (((map :token :expires_at )
305- ( gptel--gh-token gptel-backend) ))
266+ gptel--gh-token))
306267 (when (or (null token)
307268 (and expires_at
308269 (> (round (float-time (current-time )))
@@ -315,8 +276,7 @@ Then we need a session token."
315276 (header (lambda ()
316277 (gptel--gh-auth)
317278 `((" openai-intent" . " conversation-panel" )
318- (" authorization" . ,(concat " Bearer "
319- (plist-get (gptel--gh-token gptel-backend) :token )))
279+ (" authorization" . ,(concat " Bearer " (plist-get gptel--gh-token :token )))
320280 (" x-request-id" . ,(gptel--gh-uuid))
321281 (" vscode-sessionid" . ,(or (gptel--gh-sessionid gptel-backend) " " ))
322282 (" vscode-machineid" . ,(or (gptel--gh-machineid gptel-backend) " " ))
@@ -328,7 +288,8 @@ Then we need a session token."
328288 (protocol " https" )
329289 (endpoint " /chat/completions" )
330290 (stream t )
331- (models gptel--gh-models))
291+ (models gptel--gh-models)
292+ (github-token-init nil ))
332293 " Register a Github Copilot chat backend for gptel with NAME.
333294
334295Keyword arguments:
@@ -380,6 +341,9 @@ parameters (as plist keys) and values supported by the API. Use
380341these to set parameters that gptel does not provide user options
381342for."
382343 (declare (indent 1 ))
344+ (if (and (not gptel--gh-github-token)
345+ github-token-init)
346+ (gptel--gh-save (funcall github-token-init)))
383347 (let ((backend (gptel--make-gh
384348 :name name
385349 :host host
0 commit comments