+
    „WÝjT  ã                  ó‚  a € 0 t $ R t^ RIHt ^ RIt^ RIt^ RIt^ RIt^ RIt^ RI	t	^ RI
t
^ RIt^ RIt^ RIHtHt ^ RIHt ^ RIHtHtHtHtHt ^ RIHtHt ^ RIHtHtHtHtH t H!t! ^ RI"H#t# ^ R	I$H%t% ]PL                  ! R
4      t'](! 0 RDm4      t)R]*R&   REt+R R lt,R R lt-R R lt.R R lt/RFR R llt0R R lt1R R lt2R R lt3R R lt4RR/R  R! llt5R" R# lt6R$RR%RR&R/R' R( llt7R) R* lt8R+ R, lt9R-R.R/R0/R1 R2 llt: ! R3 R4];4      t<R5 R6 lt=R7R/R8 R9 llt>RGR: R; llt?R< R= lt@R>R0/R? R@ lltARAR/RB RC lltBR# )Ha  Shared device-code / loopback-PKCE / browser / TLS helpers for interactive OAuth logins.

Split out of ``hermes_cli/auth.py`` and re-exported there; origin helpers are imported lazily
inside each function so ``hermes_cli.auth.<name>`` patches still intercept (and no import cycle).
)ÚannotationsN)ÚBaseHTTPRequestHandlerÚ
HTTPServer)ÚPath)ÚAnyÚCallableÚDictÚ	FrozenSetÚOptional)Úparse_qsÚurlparse)Ú	AuthErrorÚDEFAULT_NOUS_PORTAL_URLÚ%DEVICE_AUTH_POLL_INTERVAL_CAP_SECONDSÚDEVICE_CODE_GRANT_TYPEÚOAUTH_OVER_SSH_DOCS_URLÚhttpx)ÚDeviceCodeExpired)Úis_truthy_valuezhermes_cli.authzfrozenset[str]Ú_CONSOLE_BROWSER_NAMESc               ó   € V ^8„  d   QhRR/# ©é   ÚreturnÚbool© )Úformats   "ÚOC:\Users\johnn\AppData\Local\hermes\hermes-agent\hermes_cli\auth_device_flow.pyÚ__annotate__r   2   s   € ÷ @ñ @˜Dñ @ó    c                 ó  € \        \        P                  ! R4      ;'       gc    \        P                  ! R4      ;'       gE    \        ;QJ d(    R \         4       F  '       g   K   R4      # 	  R4      # ! R \         4       4      4      # )u#  Detect environments where loopback OAuth can't reach the local browser.

Historically only SSH was checked, but #26923 surfaced that **browser-only remote consoles** (GCP Cloud
Shell, GitHub Codespaces, AWS EC2 Instance Connect, Gitpod, Replit, etc.) hit the exact same problem â€”
the user has a browser on their laptop but the loopback listener is bound on the remote VM that the
laptop's browser can't reach. These environments typically don't set ``SSH_CLIENT`` / ``SSH_TTY``, so
the SSH-only check left them with no guidance and no fallback.
Ú
SSH_CLIENTÚSSH_TTYc              3  óN   "  € T F  p\         P                  ! V4      x € K  	  R # 5i©N)ÚosÚgetenv)Ú.0Úvars   & r   Ú	<genexpr>Ú%_is_remote_session.<locals>.<genexpr>=   s   é € Ð>Ñ)= #Œr�yŠy˜�~ˆ~Ó)=ùs   ‚#%TF)r   r%   r&   ÚanyÚ_REMOTE_IDE_ENV_VARSr   r   r   Ú_is_remote_sessionr-   2   sz   € ô Ü
�	Š	�,Ó÷ 	?ð 	?¤2§9¢9¨YÓ#7÷ 	?ð 	?ß‹3Ñ>Õ)=Ó>�3Œ3ó@ð @âó@ð @àÑ>Õ)=Ó>Ó>ó@ð @r   c               ó    € V ^8„  d   QhRRRR/# )r   ÚvalueÚstrr   r   r   )r   s   "r   r   r   @   s   € ÷ Eñ E #ð E¨$ñ Er   c                óä   € V P                  4       '       d&   V P                  4       P                  4       ^ ,          MRp\        P                  P	                  V4      P                  4       \        9   # )é    Ú )ÚstripÚsplitr%   ÚpathÚbasenameÚlowerr   )r/   Útokens   & r   Ú_names_console_browserr:   @   sK   € Ø(-¯©¯ªˆE�K‰K‹M×ÑÓ! !Ö$¸2€EÜ�7‰7×Ñ˜EÓ"×(Ñ(Ó*Ô.DÑDÐDr   c               ó   € V ^8„  d   QhRR/# r   r   )r   s   "r   r   r   E   s   € ÷ Añ A Tñ Ar   c                 ó`  € \         P                  P                  RR4      p V '       d   \        V 4      '       d   R# \        P
                  P                  R4      '       db   \        \         P                  P                  R4      ;'       g     \         P                  P                  R4      4      pV'       g   V '       g   R#  \        P                  ! 4       p\        TRR4      ;'       g    \        TRR4      ;'       g    RpT;'       d    \        T4      '       * #   \         d     R# i ; i)	a§  Return True only when a *graphical* browser is likely to open.

On a headless Linux box ``webbrowser.open()`` often resolves to a text-mode browser that takes
over the terminal. Heuristics: a ``$BROWSER`` naming a console browser refuses; on Linux a
display server (``$DISPLAY`` / ``$WAYLAND_DISPLAY``) is required unless ``$BROWSER`` is set
(a console one already returned False, so a set ``$BROWSER`` here is graphical).
ÚBROWSERr3   FÚlinuxÚDISPLAYÚWAYLAND_DISPLAYÚnamer7   )r%   ÚenvironÚgetr:   ÚsysÚplatformÚ
startswithr   Ú
webbrowserÚ	ExceptionÚgetattr)Úbrowser_envÚhas_displayÚ
controllerÚ	candidates       r   Ú_can_open_graphical_browserrN   E   sÚ   € ô —*‘*—.‘. ¨BÓ/€KßÔ-¨k×:Ò:ÙÜ
‡|�|×Ñ˜w×'Ò'Üœ2Ÿ:™:Ÿ>™>¨)Ó4×YÐY¼¿
¹
¿¹ÐGXÓ8YÓZˆß§;ÙðÜ—^’^Ó%ˆ
ô ˜
 F¨BÓ/×\Ð\´7¸:ÀzÐSUÓ3V×\Ð\ÐZ\€IØ×?Ð?Ô4°YÓ?Ô@Ð@øô ô Úðús   ÃD ÄD-Ä,D-c               ó   € V ^8„  d   QhRR/# )r   r   r0   r   )r   s   "r   r   r   \   s   € ÷  ñ  ˜3ñ  r   c                 óð   €  ^ RI p V P                  4       ;'       g    Rp\        P                  ! R4      ;'       g!    \        P                  ! R4      ;'       g    RpV RV 2#   \         d    Rp LTi ; i)zWBest-effort 'user@hostname' for the SSH tunnel hint; placeholders keep it valid syntax.Nz<this-host>ÚUSERÚLOGNAMEz<user>Ú@)ÚsocketÚgethostnameÚOSErrorr%   r&   )Ú_socketÚhostnameÚusers      r   Ú_ssh_user_at_hostrZ   \   sp   € ð!Û Ø×&Ñ&Ó(×9Ð9¨Mˆô �9Š9�VÓ×@Ð@¤§	¢	¨)Ó 4×@Ð@¸€DØˆV�1�X�JÐÐøô ô !Ø Šð!ús   ‚A% �A% Á%A5Á4A5c               ó    € V ^8„  d   QhRRRR/# )r   ÚlengthÚintr   r0   r   )r   s   "r   r   r   g   s   € ÷ Zñ Z ð Z¨Sñ Zr   c                ó    € \         P                  ! \        P                  ! V 4      4      P	                  R 4      P                  R4      R,          # )ÚasciiÚ=:Né€   N)Úbase64Úurlsafe_b64encoder%   ÚurandomÚdecodeÚrstrip)r\   s   &r   Ú_pkce_code_verifierrg   g   s8   € Ü×#Ò#¤B§J¢J¨vÓ$6Ó7×>Ñ>¸wÓG×NÑNÈsÓSÐTXÕYÐYr   c               ó    € V ^8„  d   QhRRRR/# )r   Úcode_verifierr0   r   r   )r   s   "r   r   r   k   s   € ÷ Hñ H¨ð H°ñ Hr   c                óÐ   € \         P                  ! V P                  R 4      4      P                  4       p\        P
                  ! V4      P                  R4      P                  R4      # )zutf-8r_   r`   )ÚhashlibÚsha256ÚencodeÚdigestrb   rc   re   rf   )ri   rn   s   & r   Ú_pkce_code_challengero   k   sK   € Ü�^Š^˜M×0Ñ0°Ó9Ó:×AÑAÓC€FÜ×#Ò# FÓ+×2Ñ2°7Ó;×BÑBÀ3ÓGÐGr   c               ó$   € V ^8„  d   QhRRRRRR/# )r   Úexpected_pathr0   Údisplay_namer   z3tuple[type[BaseHTTPRequestHandler], dict[str, Any]]r   )r   s   "r   r   r   p   s$   € ÷ #,ñ #,Øð#,Ø),ð#,à8ñ#,r   c               óL   a aa€ RRRRRRRR/o ! VV V3R lR\         4      pVS3# )a7  Handler class for an RFC 8252 loopback redirect plus the dict it fills in.

Only a GET on *expected_path* is accepted (anything else is a 404 and leaves the result
untouched), so a nonce embedded in the path acts as the CSRF ``state`` for authorization
servers that do not echo an explicit ``state`` parameter.
ÚcodeNÚstateÚerrorÚerror_descriptionc                  ó:   <€ ] tR t^{tR V VV3R lltR R ltRtR# )ÚA_make_loopback_callback_handler.<locals>._LoopbackCallbackHandlerc               ó   € V ^8„  d   QhRR/# )r   r   ÚNoner   )r   s   "r   r   ÚN_make_loopback_callback_handler.<locals>._LoopbackCallbackHandler.__annotate__|   s   € ÷ 	Bñ 	B˜Dñ 	Br   c                	ó2  <€ \        V P                  4      pVP                  S8w  d?   V P                  R 4       V P                  4        V P                  P                  R4       R# \        VP                  4      pS F   pVP                  VR.4      ^ ,          SV&   K"  	  V P                  ^È4       V P                  RR4       V P                  4        SR,          '       d   RMRpV P                  P                  RS R	V R
2P                  4       4       R# )i”  s
   Not found.NzContent-Typeztext/html; charset=utf-8rv   ÚfailedÚreceivedz<html><body><h1>z authorization z+.</h1>You can close this tab.</body></html>)r   r6   Úsend_responseÚend_headersÚwfileÚwriter   ÚqueryrC   Úsend_headerrm   )ÚselfÚparsedÚparamsÚkeyÚoutcomerr   rq   Úresults   &    €€€r   Údo_GETÚH_make_loopback_callback_handler.<locals>._LoopbackCallbackHandler.do_GET|   sí   ø€ Ü˜dŸi™iÓ(ˆFØ�{‰{˜mÔ+Ø×"Ñ" 3Ô'Ø× Ñ Ô"Ø—
‘
× Ñ  Ô/Ùä˜fŸl™lÓ+ˆFÛ�Ø$Ÿj™j¨¨t¨fÓ5°aÕ8��s“ñ ð ×Ñ˜sÔ#Ø×Ñ˜^Ð-GÔHØ×ÑÔØ"(¨§/¤/‘h°zˆGØ�J‰J×ÑØ" < .°À¸yð I8ð 8ß8>¹»öBr   c               ó$   € V ^8„  d   QhRRRRRR/# )r   r   r0   Úargsr   r   r{   r   )r   s   "r   r   r|   �   s!   € ÷ 	ñ 	 cð 	°#ð 	¸$ñ 	r   c                	ó   € R # r$   r   )r†   r   r�   s   &&*r   Úlog_messageÚM_make_loopback_callback_handler.<locals>._LoopbackCallbackHandler.log_message�   s   € Ùr   r   N)Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__rŒ   r‘   Ú__static_attributes__)rr   rq   r‹   s   €€€r   Ú_LoopbackCallbackHandlerry   {   s   ø† ÷	Bò 	B÷(	ñ 	r   r˜   )r   )rq   rr   r˜   r‹   s   fd @r   Ú_make_loopback_callback_handlerr™   p   s<   ú€ ð % d¨G°T¸7ÀDÐJ]Ð_cÐd€F÷ñ Ô#9ô ð0 $ VÐ+Ð+r   c               ó0   € V ^8„  d   QhRRRRRRRRR	RR
R/# )r   Úhostr0   Úportr]   Úhandler_clsztype[BaseHTTPRequestHandler]ÚerrúCallable[..., AuthError]Úbind_failed_coder   r   r   )r   s   "r   r   r   –   sF   € ÷ hñ hØ
ðhØðhØ'CðhØMeðhàðhð ñhr   c          	     ó‚   €  ! R R\         4      p V! W3V4      #   \         d   pT! RT  RT RT 2T4      ThRp?ii ; i)u¯   Bind the loopback listener up front (``port=0`` = OS-assigned) so the redirect URI sent to
the authorization server names a port we already own â€” no probe-close-rebind race.c                  ó   € ] tR t^�tRtRtR# )Ú8_bind_loopback_callback_server.<locals>._ReuseHTTPServerTr   N)r“   r”   r•   r–   Úallow_reuse_addressr—   r   r   r   Ú_ReuseHTTPServerr£   �   s   † Ø"Ôr   r¥   z"Could not bind callback server on Ú:z: N)r   rV   )r›   rœ   r�   rž   r    r¥   Úexcs   &&&$$  r   Ú_bind_loopback_callback_serverr¨   –   sZ   € ô#œ:ô #ðhÙ  ¨kÓ:Ð:øÜô hÙÐ6°t°f¸A¸d¸VÀ2ÀcÀUÐKÐM]Ó^ÐdgÐgûðhús   ‘	 ›>¦9¹>c               ó0   € V ^8„  d   QhRRRRRRRRR	R
RR/# )r   Úserverr   r‹   údict[str, Any]Útimeout_secondsÚfloatrž   rŸ   Útimeout_coder0   r   r   )r   s   "r   r   r   ¦   sF   € ÷ Wñ WØðWØ .ðWØDIðWØPhðWàðWð ñWr   c               ó¨  € \         P                  ! V P                  RR/RR7      pVP                  4        \        P
                  ! 4       \        RV4      ,           p \        P
                  ! 4       V8  dk   VR,          '       g   VR,          '       d5   VV P                  4        V P                  4        VP                  RR	7       # \        P                  ! R4       K„   V P                  4        V P                  4        VP                  RR	7       V! R
V4      h  T P                  4        T P                  4        TP                  RR	7       i ; i)zZServe *server* until the redirect lands in *result* or the deadline passes; always closes.Úpoll_intervalgš™™™™™¹?T)ÚtargetÚkwargsÚdaemong      @rt   rv   ç      ð?)Útimeoutz7Authorization timed out waiting for the local callback.)Ú	threadingÚThreadÚserve_foreverÚstartÚtimeÚ	monotonicÚmaxÚshutdownÚserver_closeÚjoinÚsleep)rª   r‹   r¬   rž   r®   ÚthreadÚdeadlines   &&$$$  r   Ú_serve_loopback_callbackrÃ   ¦   s  € ô
 ×Ò V×%9Ñ%9À?ÐTWÐBXÐaeÔf€FØ
‡L�L„NÜ�~Š~Ó¤# c¨?Ó";Õ;€Hð!Ü�nŠnÓ Ô)Ø�f�~Œ~ ¨§¤Øð 	�‰ÔØ×ÑÔØ�‰˜CˆÕ ô	 �JŠJ�sŽOð *ð
 	�‰ÔØ×ÑÔØ�‰˜CˆÔ Ù
ÐGÈÓ
VÐVøð 	�‰ÔØ×ÑÔØ�‰˜CˆÕ ús   Á(D ÂD ÂD Ã	D Ä4EÚdocs_urlc               ó$   € V ^8„  d   QhRRRRRR/# )r   Úredirect_urir0   rÄ   z
str | Noner   r{   r   )r   s   "r   r   r   º   s'   € ÷ Iñ I¨3ð I¸Zð IÐSWñ Ir   c               óx  € ^ RI Hp V! 4       '       g   R#  \        V 4      pTP                  ;'       g    RTP
                  rTTR9  g	   T'       g   R# Rp\        RT RT RT  RT RT R	\        4        R
24       T'       d   \        RT 24       \        R\         RT R24       R#   \         d     R# i ; i)a  Print an SSH tunnel hint when a loopback-redirect OAuth flow runs on a remote host.

The auth server redirects the browser to ``127.0.0.1:<port>/callback``; when the browser is
on another machine (the SSH case) the redirect needs a local port forward to reach us.
)r-   Nr3   Ú
u1   
Remote session detected â€” SSH tunnel required
z-
Hermes is waiting for the OAuth callback on z‹
but your browser is on a different machine. Run this command
in a NEW terminal on your local machine BEFORE opening the URL:

  ssh -N -L z:127.0.0.1:Ú z:

Then open the authorize URL above in your local browser.zProvider docs:      zSSH/jump-box guide: >   Ú	localhostú::1ú	127.0.0.1z<------------------------------------------------------------)	Úhermes_cli.authr-   r   rH   rX   rœ   ÚprintrZ   r   )rÆ   rÄ   r-   r‡   r›   rœ   Údividers   &$     r   Ú_print_loopback_ssh_hintrÐ   º   sÞ   € õ 3Ù×ÒÙðÜ˜,Ó'ˆð —‘×&Ð& B¨¯©ˆ$ØÐ4Ô4¿DÙØ€GÜ	Ø
ˆWˆIÐHÈÈ	ð R7Ø7C°nð Eð �f˜K¨ v¨QÔ/@Ó/BÐ.Cð DCð		CôD÷ ÜÐ$ X JÐ/Ô0Ü	Ð Ô!8Ð 9¸¸G¸9ÀBÐ
GÖHøô ô Úðús   —B* Â*B9Â8B9c               ó   € V ^8„  d   QhRR/# )r   r   úbool | ssl.SSLContextr   )r   s   "r   r   r   ×   s   € ÷ ñ Ð.ñ r   c                 ó’   € ^ RI Hp Hp V ! 4       pVf   V! 4       # \        P                  R8X  d   \
        P                  ! VR7      # R# )aõ  Platform-aware default SSL verify for httpx clients.

On macOS with Homebrew Python the system OpenSSL cannot find the system trust store, so pin
certifi's bundle when importable; elsewhere defer to httpx's built-in default. The bundle is
resolved through ``agent.ssl_verify``, the one place that re-derives a path an
install-environment rotation deleted: the Nous credential exchange builds its context here, and
in a long-lived gateway it can be the first TLS client constructed after the rotation.
)Úcertifi_bundle_pathÚplatform_ssl_contextÚdarwin©ÚcafileT)Úagent.ssl_verifyrÔ   rÕ   rD   rE   ÚsslÚcreate_default_context)rÔ   rÕ   Úbundles      r   Ú_default_verifyrÝ   ×   s@   € ÷ KÙ Ó"€FØ‚~ñ $Ó%Ð%Ü
‡|�|�xÔÜ×)Ò)°Ô8Ð8Ùr   ÚinsecureÚ	ca_bundleÚ
auth_statec               ó(   € V ^8„  d   QhRRRRRRRR/# )	r   rÞ   zOptional[bool]rß   úOptional[str]rà   zOptional[dict[str, Any]]r   rÒ   r   )r   s   "r   r   r   ë   s,   € ÷ ñ ØðØ3@ðà(ðà4Iñr   c                óÔ  € ^ RI Hp \        V\        4      '       d   VP	                  R4      M/ p\        V\        4      '       d   TM/ pV e   \        V RR7      M\        VP	                  RR4      RR7      pT;'       gl    VP	                  R4      ;'       gS    \        P                  ! R4      ;'       g5    \        P                  ! R4      ;'       g    \        P                  ! R	4      pV'       d   R# V'       de   \        V4      p\        P                  P                  V4      '       g   \        P                  R
V4       V! 4       # \        P                  ! VR7      # V! 4       # )r2   )rÝ   ÚtlsF©ÚdefaultrÞ   rß   ÚHERMES_CA_BUNDLEÚSSL_CERT_FILEÚREQUESTS_CA_BUNDLEuJ   CA bundle path does not exist: %s â€” falling back to default certificatesr×   )rÍ   rÝ   Ú
isinstanceÚdictrC   r   r%   r&   r0   r6   ÚisfileÚloggerÚwarningrÚ   rÛ   )rÞ   rß   rà   rÝ   Ú	tls_stateÚeffective_insecureÚeffective_caÚca_paths   $$$     r   Ú_resolve_verifyró   ë   s(  € õ 0Ü)3°JÄ×)EÒ)E�
—‘˜uÔ%È2€IÜ'¨	´4×8Ò8‘	¸b€Ià4<Ò4HŒ˜¨%Õ0Ü˜YŸ]™]¨:°uÓ=ÀuÔMð ð 	÷ 	Ið 	I�Y—]‘] ;Ó/÷ 	Ið 	I´2·9²9Ð=OÓ3P÷ 	Ið 	IÜ�9Š9�_Ó%÷	Ið 	IÜ)+¯ªÐ3GÓ)Hð ÷ ÙßÜ�lÓ#ˆÜ�w‰w�~‰~˜g×&Ò&Ü�N‰NØ\Øôñ #Ó$Ð$Ü×)Ò)°Ô9Ð9ÙÓÐr   c          
     ó,   € V ^8„  d   QhRRRRRRRRRR	/# )
r   Úclientúhttpx.ClientÚportal_base_urlr0   Ú	client_idÚscoperâ   r   r«   r   )r   s   "r   r   r     s4   € ÷ ñ ØðØ+.ðØ;>ðØGTðàñr   c                ó   € T P                  V R2RV/V'       d   RV/M/ CR7      pVP                  4        VP                  4       p. ROpV Uu. uF  qwV9  g   K  VNK  	  ppV'       d   \        RRP	                  V4       24      hV# u upi )zFPOST to the device code endpoint. Returns device_code, user_code, etc.z/api/oauth/device/coderø   rù   ©Údataz%Device code response missing fields: z, )Údevice_codeÚ	user_codeÚverification_uriÚverification_uri_completeÚ
expires_inÚinterval)ÚpostÚraise_for_statusÚjsonÚ
ValueErrorr¿   )	rõ   r÷   rø   rù   Úresponserü   Úrequired_fieldsÚfÚmissings	   &&&&     r   Ú_request_device_coder    s    € ð �{‰{ØÐ
Ð1Ð2Ø˜9ÐL¿U¨'°5Ñ)9ÈÐLð ó N€Hð ×ÑÔØ�=‰=‹?€Dò€Oñ *Ó;™/�Q°d©]�qˆq™/€GÐ;ßÜÐ@ÀÇÁÈ7ÓASÐ@TÐUÓVÐVØ€Kùò <s   ÁBÁBc               ó    € V ^8„  d   QhRRRR/# )r   r÷   r0   r   r   )r   s   "r   r   r     s   € ÷ +ñ +°sð +¸sñ +r   c                óL   € T ;'       g    \         P                  R4      pRV R2# )ar  Actionable timeout text: the usual cause is Portal sign-in failing in the browser tab.

A bare "Timed out waiting for device authorization" gives the user nothing to act on. The most common
cause is Portal sign-in failing in the opened browser tab (including the server-side CAPTCHA loop from
20605), so point at the Portal login page and the retry command. See #20605.
Ú/a   Timed out waiting for device authorization.
  Portal sign-in is required before the device code can be approved.
  If the browser showed a CAPTCHA / 'You did not pass CAPTCHA' error,
  finish signing in at the Portal in a normal browser tab, then retry:
    hermes portal
  Portal login: z/login)r   rf   )r÷   Úportals   & r   Ú!_nous_device_auth_timeout_messager    s5   € ð ×8Ð8Ô!8×@Ñ@ÀÓE€Fð	ð
 "˜( &ð	*ð+r   Úfailure_dashz--Úswallow_open_errorsFc               ó0   € V ^8„  d   QhRRRRRRRRRRRR	/# )
r   Úverification_urlr0   rþ   Úopen_browserr   r  r  r   r{   r   )r   s   "r   r   r   '  sD   € ÷ [ñ [Øð[Ø&)ð[Ø<@ð[ØPSð[àð[à*.ñ[r   c               ó*  € \        4        \        R4       \        RV  24       \        RV 24       V'       g   R#  \        P                  ! V 4      pV'       d   \        R4       R# \        RV R24       R#   \         d    T'       g   h Rp L>i ; i)	zôPrint the shared "To continue" device-code block and optionally open the browser.

Callers decide *whether* to open (remote-session / graphical-browser gating differs per
provider); *failure_dash* keeps each provider's historical hint wording.
zTo continue:z  1. Open: z  2. If prompted, enter code: NFz#  (Opened browser for verification)z'  Could not open browser automatically z use the URL above.)rÎ   rG   ÚopenrH   )r  rþ   r  r  r  Úopeneds   &&$$$ r   Ú_print_device_code_instructionsr  '  s‰   € ô 
„GÜ	ˆ.ÔÜ	ˆKÐ(Ð)Ð
*Ô+Ü	Ð*¨9¨+Ð
6Ô7ßÙðÜ—’Ð!1Ó2ˆ÷
 ÜÐ3Ö4äÐ7¸°~ÐEXÐYÖZøô ô ß"ØØŠðús   ½A9 Á9BÂBÂBc                  ó   € ] tR tRtRtRtR# )ÚDeviceFlowCancellediA  zGThe caller's ``cancelled`` hook fired while the token poll was waiting.r   N)r“   r”   r•   r–   Ú__doc__r—   r   r   r   r  r  A  s   † ÝQr   r  c               ó$   € V ^8„  d   QhRRRRRR/# )r   Úsecondsr­   Ú	cancelledúOptional[Callable[[], bool]]r   r{   r   )r   s   "r   r   r   E  s#   € ÷  ñ   ð  Ð2Nð  ÐSWñ  r   c                ó.  € Vf   \         P                  ! V 4       R# \         P                  ! 4       V ,           pV! 4       '       gH   V\         P                  ! 4       ,
          pV^ 8:  d   R# \         P                  ! \        RV4      4       KT  \	        4       h)zXSleep *seconds*; with a hook, in <= 1 s ticks so a cancel ends the poll within a second.Nr´   )rº   rÀ   r»   Úminr  )r  r  ÚwakeÚ	remainings   &&  r   Ú_cancellable_sleepr%  E  sf   € àÒÜ�
Š
�7ÔÙÜ�>Š>Ó˜gÕ%€DÙ�kŠkØœ4Ÿ>š>Ó+Õ+ˆ	Ø˜Œ>ÙÜ�
Š
”3�s˜IÓ&Ö'Ü
Ó
Ðr   r  c               ó<   € V ^8„  d   QhRRRRRRRRRR	R
RRRRRRR/	# )r   r  zCallable[[], httpx.Response]r  r]   r°   Úvalidate_successz Callable[[dict[str, Any]], None]Úon_non_json_errorz%Callable[[httpx.Response], Exception]Úon_errorz5Callable[[httpx.Response, dict[str, Any]], Exception]Ú
on_timeoutzCallable[[], Exception]r  r   r   r«   r   )r   s   "r   r   r   S  sm   € ÷ 9vñ 9vØ
&ð9vØ7:ð9vØKNð9và6ð9vð =ð9vð Dð	9vð
 (ð9vð
 5Qð9vð
 ]kñ9vr   c               óº  € \         P                  ! 4       \        ^V4      ,           pTp	Rp
^ p\         P                  ! 4       V8  dù   Ve   V! 4       '       d   \        4       hV ! 4       pVP                  ^È8X  d   VP                  4       pV! V4       V#  VP                  4       pR^ rº\        TP                  R4      ;'       g    R4      pTR	8X  d=   \        \        T	\        RT\         P                  ! 4       ,
          4      4      T4       Kâ  TR
8X  d"   \        T	^,           ^4      p	\        Y—4       EK
  T! YÎ4      hV! 4       V'       d   \        RV 24      hRh  \         dó    TP                  pTR9   g0   TR8¼  g)   TR8X  d¹   TP                  P                  R4      '       d˜   ^ RI	H
p TpT! TP                  4      pTe   \        \        T	T4      ^<4      p
M%\        \        T
^,          T	^,          ^4      ^<4      p
\        \        R\        Y¨\         P                  ! 4       ,
          4      4      T4        EK  TP                  4        T! T4      hi ; i)uÖ  RFC 8628 device-code polling loop shared by the Nous and xAI flows.

``authorization_pending`` sleeps and retries; ``slow_down`` grows the interval by 1s (cap 30s).
A non-JSON 408/429/5xx, or a 403 carrying ``x-vercel-mitigated`` (edge/WAF mitigation, never a
real OAuth error), backs off â€” honoring ``Retry-After``, capped at 60s and at the device-code
deadline â€” instead of aborting a login the user may still be approving. Every other error, a
non-JSON error body, and the deadline become provider-specific exceptions via the supplied
factories so each caller keeps its exact error contract. *cancelled* is checked before every
request and during every wait; once it fires the poll raises :class:`DeviceFlowCancelled`.
g        Niô  i“  zx-vercel-mitigated)Úparse_retry_after_secondsrv   r3   Úauthorization_pendingÚ	slow_downztoken endpoint answered HTTP >   é˜  é­  )rº   r»   r¼   r  Ústatus_coder  rH   ÚheadersrC   Úagent.retry_utilsr,  r"  r%  r  r0   ÚConnectionError)r  r  r°   r'  r(  r)  r*  r  rÂ   Úcurrent_intervalÚedge_backoffÚunavailabler  ÚpayloadÚerror_payloadÚstatusr,  Úretry_afterÚ
error_codes   &$$$$$$$           r   Ú_poll_device_token_genericr=  S  s  € ô  �~Š~Ó¤# a¨Ó"4Õ4€HØ$ÐØ€LØ€KÜ
�.Š.Ó
˜XÔ
%ØÒ ¡Y§[¢[Ü%Ó'Ð'Ù“6ˆØ×Ñ 3Ô&Ø—m‘m“oˆGÙ˜WÔ%ØˆNð	.Ø$ŸM™M›OˆMð" %(¨�kÜ˜×*Ñ*¨7Ó3×9Ð9°rÓ:ˆ
ØÐ0Ô0ÜœsÐ#3´S¸¸hÌÏÊÓIYÕ>YÓ5ZÓ[Ð]fÔgÙØ˜Ô$Ü"Ð#3°aÕ#7¸Ó<ÐÜÐ/Ô;ÚÙ�xÓ/Ð/á
‹,×_jœ_Ð/LÈ[ÈMÐ-ZÓ[ÐuÐptÐuøô7 ô 	.Ø×)Ñ)ˆFà˜Ô# v°¤}Ø˜c”M h×&6Ñ&6×&:Ñ&:Ð;O×&PÒ&PÝGØ$�Ù7¸×8HÑ8HÓI�ØÒ*Ü#&¤sÐ+;¸[Ó'IÈ2Ó#N‘Lä#&¤s¨<¸!Õ+;Ð=MÐPQÕ=QÐSTÓ'UÐWYÓ#Z�LÜ"¤3 s¬C°ÌÏÊÓIYÕ>YÓ,ZÓ#[Ð]fÔgÛØ×%Ñ%Ô'Ù# HÓ-Ð-ð	.ús   ÂE ÅAIÆ*BIÉIc               ó8   € V ^8„  d   QhRRRRRRRRRRR	RR
RRR/# )r   rõ   rö   r÷   r0   rø   rý   r  r]   r°   r  r   r   r«   r   )r   s   "r   r   r   �  sL   € ÷ ñ ØðØ+.ðØ;>ðØMPðàðà$'ðà4Pðà\jñr   c                óŽ   a aaa€ R R lpR R lp\        V VVV3R lV\        ^\        V\        4      4      WxR V3R lVR7      # )	zIPoll the Nous token endpoint until the user approves or the code expires.c               ó    € V ^8„  d   QhRRRR/# )r   r8  r«   r   r{   r   )r   s   "r   r   Ú%_poll_for_token.<locals>.__annotate__“  s   € ÷ Lñ L˜>ð L¨dñ Lr   c                ó*   € R V 9  d   \        R4      hR# )Úaccess_tokenz+Token response did not include access_tokenN)r  )r8  s   &r   Ú	_validateÚ"_poll_for_token.<locals>._validate“  s   € Ø Ô(ÜÐJÓKÐKñ )r   c               ó   € V ^8„  d   QhRR/# )r   r   rH   r   )r   s   "r   r   rA  —  s   € ÷ [ñ [¨Iñ [r   c                óª   € ^ RI Hp T! \        VP                  RR4      ;'       g    R4      \        VP                  R4      ;'       g    R4      4      # )r2   )Údevice_flow_errorrv   r3   rw   zUnknown authentication error)Úhermes_cli.auth_error_copyrH  r0   rC   )Ú	_responser9  rH  s   && r   Ú_errorÚ_poll_for_token.<locals>._error—  sN   € å@Ù Ü�×!Ñ! '¨2Ó.×4Ð4°"Ó5Ü�×!Ñ!Ð"5Ó6×XÐXÐ:XÓYó[ð 	[r   c            	     óD   <€ S P                  S R 2R\        RSRS/R7      # )z/api/oauth/tokenÚ
grant_typerø   rý   rû   )r  r   )rõ   rø   rý   r÷   s   €€€€r   Ú<lambda>Ú!_poll_for_token.<locals>.<lambda>Ÿ  s3   ø€ �—‘ØÐÐ/Ð0àÔ4°kÀ9Ø˜{ð,ð ô -r   c                ó   € \        R 4      # )z1Token endpoint returned a non-JSON error response)ÚRuntimeError)Ú_rs   &r   rO  rP  §  s   € ¤\Ø?ô&Ar   c                 ó,   <€ \        \        S 4      4      # r$   )r   r  )r÷   s   €r   rO  rP  «  s   ø€ Ô,Ô-NÈÓ-_Ô`r   )r  r°   r'  r)  r(  r*  r  )r=  r¼   r"  r   )	rõ   r÷   rø   rý   r  r°   r  rD  rK  s	   ffff&&&  r   Ú_poll_for_tokenrU  �  sL   û€ õLõ[ô &÷	-ð
 Ü˜!œS Ô0UÓVÓWØ"ñAô aØôð r   c               ó$   € V ^8„  d   QhRRRRRR/# )r   Úpromptr0   ræ   r   r   r   )r   s   "r   r   r   ¯  s&   € ÷ Tñ T˜3ð T¨Cð T°Dñ Tr   c               ó¦   €  \        V 4      P                  4       P                  4       pVR8X  d   VR9   # VR9   #   \        \        3 d    Tp L&i ; i)zB``input()`` a [Y/n]-style question; EOF/Ctrl-C count as *default*.Úy>   r3   rY  Úyes>   rY  rZ  )Úinputr4   r8   ÚEOFErrorÚKeyboardInterrupt)rW  ræ   Úanswers   &$ r   Ú_prompt_yes_nor_  ¯  s\   € ðÜ�v“×$Ñ$Ó&×,Ñ,Ó.ˆð *1°C¬ˆ6Ð%Ñ%ÐS¸VÀ|Ñ=SÐSøô Ô'Ð(ô ØŠðús   ‚': ºAÁAÚshow_auth_statec               ó(   € V ^8„  d   QhRRRRRRRR/# )	r   Úprovider_idr0   Úconfig_pathr   r`  r   r   r{   r   )r   s   "r   r   r   ¸  s2   € ÷ Mñ MØðMØ#'ðMØ=AðMØNRñMr   c               ó˜   € \        4        \        R 4       V'       d   ^ RIHp \        RV! 4        R24       \        RV RV  R24       R# )zLogin successful!)Údisplay_hermes_homez  Auth state: z
/auth.jsonz  Config updated: z (model.provider=Ú)N)rÎ   Úhermes_constantsre  )rb  rc  r`  Ú_dhhs   &&$ r   Ú_print_login_successri  ¸  sB   € ä	„GÜ	Ð
ÔßÝ@Ü�™t›v˜h jÐ1Ô2Ü	Ð˜{˜mÐ+<¸[¸MÈÐ
KÖLr   Úexpired_noticec               ó4   € V ^8„  d   QhRRRRRRRRRRR	R
RR/# )r   rb  r0   ÚresolvezCallable[[], dict[str, Any]]Úis_expiringzCallable[[str, int], bool]rr   Údefault_base_urlrj  râ   r   r   r   )r   s   "r   r   r   Â  sH   € ÷ ñ ØðØ">ðà+ðà;>ðàRUðð "ðð .2ñr   c               ó~  € ^ RI Hp  V! 4       pVP                  RR4      p\        V\        4      '       db   V'       dZ   V! V^<4      '       gK   \        RV R24       \        RRR7      '       d&   V! WP                  R	V4      4      p	\        W	4       R
#  R# V'       d   \        V4       R#   \         d     R# i ; i)uê   Offer to reuse still-valid stored OAuth credentials. Returns True when the user accepted.

*resolve* attempts a refresh, so a resolved token should be valid â€” but double-check the
expiry before telling the user "Login successful!".
)Ú_update_config_for_providerÚapi_keyr3   z	Existing z( credentials found in Hermes auth store.z!Use existing credentials? [Y/n]: rY  rå   Úbase_urlTF)	rÍ   rp  rC   rê   r0   rÎ   r_  ri  r   )
rb  rl  rm  rr   rn  rj  rp  Úexistingrq  rc  s
   &$$$$$    r   Ú!_offer_existing_oauth_credentialsrt  Â  s·   € õ <ðÙ“9ˆØ—,‘,˜y¨"Ó-ˆÜ�gœs×#Ò#¯¹ÀGÈR×8PÒ8PÜ�I˜l˜^Ð+SÐTÔUÜÐAÈ3×OÓOÙ9Ø§¡¨jÐ:JÓ!KóM�ä$ [Ô>Ùð	 Pñ ÷	 Ü�.Ô!ñ øô ô ØÙðús)   ˆ6B- ¿B- ÁAB- ÂB- Â B- Â-B<Â;B<>   Úw3mÚlynxÚlinksÚbrowshÚelinksÚlinks2úwww-browser)ÚCLOUD_SHELLÚ
CODESPACESÚCODESPACE_NAMEÚGITPOD_WORKSPACE_IDÚREPL_IDÚ
STACKBLITZ)é@   r$   )CÚ__conditional_annotations__r  Ú
__future__r   rb   rk   Úloggingr%   rÚ   rD   r¶   rº   rG   Úhttp.serverr   r   Úpathlibr   Útypingr   r   r   r	   r
   Úurllib.parser   r   Úhermes_cli.auth_constantsr   r   r   r   r   r   rI  r   Úutilsr   Ú	getLoggerrí   Ú	frozensetr   Ú__annotations__r,   r-   r:   rN   rZ   rg   ro   r™   r¨   rÃ   rÐ   rÝ   ró   r  r  r  rH   r  r%  r=  rU  r_  ri  rt  )rƒ  s   @r   Ú<module>r�     s]  øðôõ #ã Û Û Û 	Û 
Û 
Û Û Û ß :Ý ß ;Õ ;ß +÷<÷ <õ 9Ý !ð 
×	Ò	Ð,Ó	-€ñ
 *3ò 4ó *Ð ˜ó ðÐ õ@õEõ
Aõ. ÷ZõHõ
#,õLhõ Wð(IÈ$÷ Iõ:ð(Ø"&ðØCGðà+/÷õ2õ$+ð"[ØVZð[à %÷[ô4R˜)ô Rõ ð9vð
 TX÷9v÷xõ@TðMØDI÷Mðð %)÷ñ r   