Ë
    âß²jÛo  ã                  óh  — d Z ddlmZ ddlZddlZddlZddlZddlZddlm	Z	 ddl
mZmZmZmZ ddlmZ ddlmZmZ ddlmZmZmZmZmZmZmZmZmZmZmZm Z  dd	l!m"Z" erdd
l#m$Z$  ejJ                  d«      Z&dZ'd3d„Z(d4d„Z)d5d„Z*d6d„Z+ddœd7d„Z,d8d„Z-ddddddœ	 	 	 	 	 	 	 	 	 	 	 	 	 d9d„Z.d:d„Z/d;d<d„Z0d=d„Z1d>d„Z2d?d„Z3d@d„Z4dAd„Z5dBdCd „Z6dDd!„Z7dd"d#œ	 	 	 	 	 	 	 	 	 dEd$„Z8dd%œ	 	 	 	 	 	 	 	 	 dFd&„Z9dGd'„Z:dHd(„Z;d)ddd*œ	 	 	 	 	 	 	 dId+„Z<d)d,œdJd-„Z=ed.œdKd/„Z>	 	 	 	 	 	 	 	 	 	 	 	 dLd0„Z?d"dd1œdMd2„Z@y)NzæxAI Grok OAuth: token store, discovery, refresh, device-code login.

Split out of ``hermes_cli/auth.py``; origin helpers are imported lazily per function so
``hermes_cli.auth.<helper>`` patches still intercept and no cycle forms.
é    )ÚannotationsN)ÚPath)ÚAnyÚDictÚOptionalÚTYPE_CHECKING)Úurlparse)Ú_load_auth_store_maybe_lockedÚ_refresh_payload_access_token)ÚAUTH_LOCK_TIMEOUT_SECONDSÚ	AuthErrorÚDEFAULT_XAI_OAUTH_BASE_URLÚDEVICE_CODE_GRANT_TYPEÚ%XAI_ACCESS_TOKEN_REFRESH_SKEW_SECONDSÚXAI_OAUTH_CLIENT_IDÚXAI_OAUTH_DEVICE_CODE_URLÚXAI_OAUTH_DISCOVERY_URLÚXAI_OAUTH_SCOPEÚ_FORM_JSON_HEADERSÚ_xai_errÚhttpx)Ú	env_float)ÚProviderConfigzhermes_cli.authz$Re-authenticate with `hermes model`.c                ó<   — t        | xs d«      j                  «       S )NÚ )ÚstrÚstrip)Úvalues    ú8/home/ubuntu/.hermes/hermes-agent/hermes_cli/auth_xai.pyÚ_cleanr        s   € ÜˆuŠ{˜Ó×!Ñ!Ó#Ð#ó    c                óŒ   — t        | t        «      syt        | j                  d«      «      t        | j                  d«      «      fS )zV``(access_token, refresh_token)`` stripped; empty strings when *tokens* is not a dict.)r   r   Úaccess_tokenÚrefresh_token)Ú
isinstanceÚdictr    Úget)Útokenss    r   Ú_token_pairr)   $   s6   € ä�fœdÔ#ØÜ�&—*‘*˜^Ó,Ó-¬v°f·j±jÀÓ6QÓ/RÐRÐRr!   c                óN  — ddl m}  || d«      }t        |t        «      r%t	        t        |j                  d«      «      «      r|S | j                  d«      }t        |t        «      r|j                  d«      nd}t        |t        «      r|ndD ]ƒ  }t        |«      \  }}|r|sŒt        |xs i «      }||t        |j                  d«      xs d	«      d
œ|d<   |j                  d«      r|j                  d«      |d<   |j                  dd«       |c S  t        |t        «      r|S dS )zEReturn usable xAI OAuth state from provider state or credential pool.r   )Ú_load_provider_stateú	xai-oauthr(   Úcredential_poolN© Ú
token_typeÚBearer)r#   r$   r/   Úlast_refreshÚ	auth_modeÚ
oauth_pkce)
Úhermes_cli.authr+   r%   r&   Úallr)   r'   Úlistr   Ú
setdefault)	Ú
auth_storer+   Ústater-   ÚentriesÚentryr#   r$   Úmergeds	            r   Ú_xai_oauth_state_from_storer=   +   s  € å4Ù  ¨[Ó9€EÜ�%œÔ¤3¤{°5·9±9¸XÓ3FÓ'GÔ#HØˆà —n‘nÐ%6Ó7€OÜ2<¸_ÌdÔ2Sˆo×!Ñ! +Ô.ÐY]€GÜ& w´Ô5‘¸2ò ˆÜ&1°%Ó&8Ñ#ˆ�mÙ¡=ØÜ�e’k˜rÓ"ˆà(¸=Ü˜eŸi™i¨Ó5ÒA¸ÓBñ
ˆˆxÑð �9‰9�^Ô$Ø%*§Y¡Y¨~Ó%>ˆF�>Ñ"Ø×Ñ˜+ |Ô4ØŠðô ˜u¤dÔ+ˆ5Ð5°Ð5r!   c                ól   — t        | t        «      xr# t        t        | j	                  d«      «      «      S )Nr(   )r%   r&   r5   r)   r'   )r9   s    r   Ú"_xai_oauth_state_has_usable_tokensr?   E   s'   € Ü�eœTÓ"ÒL¤s¬;°u·y±yÀÓ7JÓ+KÓ'LÐLr!   T©Ú_lockc                óø  — ddl m} t        t        | «      «      }t	        |«      st         |«       «      }t	        |«      r|}|st        ddd¬«      ‚|j                  d«      }t        |t        «      st        dt        › �d	d¬«      ‚t        |«      \  }}|d
f|dffD ]"  \  }}|rŒ	t        d|› dt        › �d|› �d¬«      ‚ ||j                  d«      |j                  d«      xs i |j                  d«      dœS )Nr   )Ú_load_global_auth_storez`No xAI OAuth credentials stored. Select xAI Grok OAuth (SuperGrok / Premium+) in `hermes model`.Úxai_auth_missingT©Úreloginr(   z#xAI OAuth state is missing tokens. Úxai_auth_invalid_shaper#   r$   zxAI OAuth state is missing z. Úxai_auth_missing_r1   Ú	discoveryÚredirect_uri)r(   r1   rI   rJ   )r4   rC   r=   r
   r?   r   r'   r%   r&   Ú_RELOGINr)   )	rA   rC   r9   Úglobal_stater(   r#   r$   r   Úfields	            r   Ú_read_xai_oauth_tokensrN   I   s  € Ý7Ü'Ô(EÀeÓ(LÓM€EÜ-¨eÔ4Ü2Ñ3JÓ3LÓMˆÜ-¨lÔ;Ø ˆEÙÜØnØ¨ô
ð 	
ð �Y‰Y�xÓ €FÜ�fœdÔ#ÜÐ<¼X¸JÐGÐIaÐkoÔpÐpÜ"-¨fÓ"5Ñ€L�-Ø&¨Ð7¸-ÈÐ9YÐZò ‰ˆˆuÚÜØ-¨e¨W°B´x°jÐAÐEVÐW\ÐV]ÐC^Ðhlôð ðð ¨%¯)©)°NÓ*CØ—Y‘Y˜{Ó+Ò1¨rÀ5Ç9Á9È^ÓC\ñð r!   c                ó°  — ddl m}m}  |«       }|€yt        j                  j                  d«      r t        j                  j                  dd«      nd}|r9t        |«      dz  dz  }	 |j                  d	¬
«      |j                  d	¬
«      k(  ry	 	  |d| |d	¬«       y# t        $ r Y yw xY w# t        $ r }t        j                  d|«       Y d}~yd}~ww xY w)aY  Best-effort persist of a rotated xAI grant into the global-root auth.json.

    xAI rotates refresh_token on every refresh, so a profile that refreshed a root-resolved grant
    must write the chain back to root. Touches only root ``providers.xai-oauth``; swallows all
    errors (root-stale is better than breaking the profile's own save).
    r   )Ú_global_auth_file_pathÚ _persist_provider_state_to_storeNÚPYTEST_CURRENT_TESTÚHOMEr   z.hermesz	auth.jsonF)Ústrictr,   ©Ú
set_activez2xAI OAuth: write-through to global root failed: %s)r4   rP   rQ   ÚosÚenvironr'   r   ÚresolveÚ	ExceptionÚloggerÚdebug)r9   rP   rQ   Úglobal_pathÚreal_home_envÚ	real_rootÚexcs          r   Ú'_write_through_xai_oauth_to_global_rootra   d   sÙ   € ÷ YÙ(Ó*€KØÐØô 35·*±*·.±.ÐAVÔ2W”B—J‘J—N‘N 6¨2Ô.Ð]_€MÙÜ˜Ó'¨)Ñ3°kÑAˆ	ð	Ø×"Ñ"¨%Ð"Ó0°I×4EÑ4EÈUÐ4EÓ4SÒSØð TðPÙ(¨°e¸[ÐUZÖ[øô ò 	Ùð	ûô ò PÜ�‰ÐIÈ3×OÑOûðPús*   Á(%B ÂB, Â	B)Â(B)Â,	CÂ5CÃCr   Úoauth_device_code©rI   rJ   r1   r2   rV   c               óR  — ddl m}m}m}m}	m}
m}m}m}m	} |€ |«       } |«       5   |«       } |	|d«      \  }}|�|ni }|j                  | ||¬«       |r||d<   |r||d<    |«       }|�|� |
||«      r	 ||«       n ||d||¬«        ||«       ddd«       y# 1 sw Y   yxY w)	zëPersist xAI OAuth tokens; *set_active* also promotes ``xai-oauth`` to ``active_provider``.

    Pass ``set_active=False`` for side-tool bootstrap (TTS/setup, tools config, dashboard, refresh)
    so inference routing is unchanged.
    r   )	Ú_auth_store_lockrP   Ú_load_auth_storeÚ _load_provider_state_with_sourceÚ
_same_pathÚ_save_auth_storeÚ_store_provider_stateÚ
_utc_now_zra   Nr,   )r(   r1   r2   rI   rJ   rU   )r4   re   rP   rf   rg   rh   ri   rj   rk   ra   Úupdate)r(   rI   rJ   r1   r2   rV   re   rP   rf   rg   rh   ri   rj   rk   ra   r8   r9   Úsource_pathÚglobal_roots                      r   Ú_save_xai_oauth_tokensro      sÊ   € ÷ g÷  gõ  gØÐÙ!“|ˆÙ	Ó	ñ )Ù%Ó'ˆ
ñ >¸jÈ+ÓVÑˆˆ{ØÐ*‘°ˆØ�‰˜F°ÈˆÔSÙØ!*ˆE�+ÑÙØ$0ˆE�.Ñ!Ù,Ó.ˆØÐ" {Ð'>Á:ÈkÐ[fÔCgá3°EÕ:á! *¨k¸5ÈZÕXÙ˜ZÔ(÷%)÷ )ñ )ús   §A-BÂB&c                ó²  — t        | t        «      rd| vry	 | j                  d«      }t        |«      dk  ry|d   dt        |d   «       dz  z  z   }t	        j
                  t        j                  |j                  d«      «      j                  d«      «      j                  d	«      }t        |t        t        f«      rt        |«      S dS # t        $ r Y yw xY w)
zL``exp`` claim of a JWT-shaped access token, or None when absent/undecodable.ú.Né   é   ú=é   Úasciizutf-8Úexp)r%   r   ÚsplitÚlenÚjsonÚloadsÚbase64Úurlsafe_b64decodeÚencodeÚdecoder'   ÚintÚfloatrZ   )r#   ÚpartsÚpayload_b64rw   s       r   Ú_xai_jwt_expr„   ¡   sÅ   € ä�l¤CÔ(¨C°|Ñ,CØðØ×"Ñ" 3Ó'ˆÜˆu‹:˜Š>ØØ˜A‘h ¬¨U°1©X«¨¸Ñ(:Ñ!;Ñ;ˆÜ�j‰jœ×1Ñ1°+×2DÑ2DÀWÓ2MÓN×UÑUÐV]Ó^Ó_×cÑcÐdiÓjˆÜ'¨¬c´5¨\Ô:Œu�S‹zÐDÀÐDøÜò Ùðús   —C
 ·BC
 ÃC
 Ã
	CÃCc                ó~   — t        | «      }|d uxr- |t        j                  «       t        dt        |«      «      z   k  S )Nr   )r„   ÚtimeÚmaxr€   )r#   Úskew_secondsrw   s      r   Ú_xai_access_token_is_expiringr‰   °   s7   € Ü
�|Ó
$€CØ�dˆ?ÒM˜s¤d§i¡i£k´C¸¼3¸|Ó;LÓ4MÑ&MÑMÐMr!   c                ó’   — t         }t        | «      }|€|S |t        j                  «       z
  }d|cxk  rdk  rn nt        d|«      S |S )zöProactive-refresh lead time before JWT ``exp``.

    Device-code logins often return ~15-minute JWTs; the full hour-long skew would refresh on every
    resolution, burning single-use refresh tokens and racing callers into ``invalid_grant``.
    r   iŒ
  éx   )r   r„   r†   Úmin)r#   Úmax_skewrw   Ú	remainings       r   Ú#_xai_proactive_refresh_skew_secondsr�   µ   sK   € ô 5€HÜ
�|Ó
$€CØ
€{ØˆØ”d—i‘i“kÑ!€IØ!" YÔ!9°'Õ!9Œ3ˆs�HÓÐG¸xÐGr!   c                ó2   — | dk(  xs | j                  d«      S )zQ``x.ai`` is the bare apex, so an exact match or any ``.x.ai`` suffix is accepted.zx.aiz.x.ai)Úendswith)Úhosts    r   Ú_is_xai_origin_hostr“   Ã   s   € à�6‰>Ò3˜TŸ]™]¨7Ó3Ð3r!   c                ó¬   — t        | «      }|j                  xs dj                  «       }|j                  dk7  rd|fS |sd|fS t	        |«      sd|fS d|fS )un   ``(problem, host)`` â€” problem is ``"scheme"``, ``"host"``, ``"origin"`` or None when *url* is HTTPS on x.ai.r   ÚhttpsÚschemer’   ÚoriginN)r	   ÚhostnameÚlowerr–   r“   )ÚurlÚparsedr’   s      r   Ú_xai_url_problemrœ   È   sa   € ä�c‹]€FØ�O‰OÒ!˜r×(Ñ(Ó*€DØ‡}�}˜ÒØ˜ˆ~ÐÙØ�tˆ|ÐÜ˜tÔ$Ø˜ˆ~ÐØ�ˆ:Ðr!   c               óz   — t        | «      \  }}|€| S d|› d| ›d�d|› d| ›d�d|› d|›d�dœ|   }t        |d	«      ‚)
u  Refuse a discovery endpoint that isn't HTTPS on the xAI origin.

    Discovery is cached in auth.json, so one MITM at login could plant a ``token_endpoint`` that
    receives the refresh_token forever; pinning scheme + host (RFC 8414 Â§2) removes that.
    z(xAI OIDC discovery returned a non-HTTPS z: rq   zxAI OIDC discovery z is missing a hostname: z host zÖ is not on the xAI origin (expected x.ai or a *.x.ai subdomain). Refusing to use a cached endpoint that may have been substituted by a MITM during initial discovery; re-authenticate with `hermes model` to re-fetch.)r–   r’   r—   Úxai_discovery_invalid)rœ   r   )rš   rM   Úproblemr’   Úmessages        r   Ú_xai_validate_oauth_endpointr¡   Õ   s{   € ô % SÓ)�M€GˆTØ€Øˆ
à<¸U¸GÀ2ÀcÀWÈAÐNØ% e WÐ,DÀSÀGÈ1ÐMà! % ¨¨t¨hð 7Jð Kñ		ð ñ	€Gô �7Ð3Ó
4Ð4r!   c               ór  — | xs dj                  «       j                  d«      }|s|S 	 t        |«      \  }}|€|S |dk(  rt        j                  d||«       |S |dk(  rt        j                  d||«       |S t        j                  d|||«       |S # t        $ r t        j                  d||«       |cY S w xY w)	z¿Pin the OAuth inference base_url to ``*.x.ai``; warn and use *fallback* on rejection.

    Warn-not-raise: a bad env var must not deadlock auth, but the bearer must never leak elsewhere.
    r   ú/z>Ignoring malformed xAI base_url override %r; using %s instead.r–   znRefusing non-HTTPS xAI base_url override %r (xai-oauth bearer would be sent in cleartext); falling back to %s.r’   zEIgnoring xAI base_url override %r with no hostname; using %s instead.uñ   Refusing xAI base_url override %r â€” host %r is not on the xAI origin (expected x.ai or a *.x.ai subdomain). The xai-oauth bearer is only valid against xAI's inference API; sending it elsewhere would leak the credential. Falling back to %s.)r   Úrstriprœ   rZ   r[   Úwarning)r   ÚfallbackÚ	candidaterŸ   r’   s        r   Ú _xai_validate_inference_base_urlr¨   ë   sÞ   € ð
 ’˜"×#Ñ#Ó%×,Ñ,¨SÓ1€IÙØˆðÜ(¨Ó3‰ˆ�ð €ØÐØ�(ÒÜ�‰ð9à�xô	
ð €Oð 
�FÒ	Ü�‰Ð^Ð`iÐksÔtð €Oô 	�‰ð2ð �t˜Xô	
ð €Oøô+ ò Ü�‰ÐWÐYbÐdlÔmØŠðús   ©B Â"B6Â5B6c           	     ób  — 	 t        j                  t        ddi| ¬«      }|j
                  dk7  rt	        d|j
                  › d�d«      ‚	 |j                  «       }t        |t        «      st	        dd«      ‚dD �ci c]  }|t        |j                  |«      «      “Œ }}t        |j                  «       «      st	        dd«      ‚|j                  «       D ]  \  }}t        ||¬«       Œ |S # t        $ r}t	        d|› �d«      |‚d }~ww xY w# t        $ r}t	        d	|› �d
«      |‚d }~ww xY wc c}w )NÚAcceptúapplication/json)ÚheadersÚtimeoutzxAI OIDC discovery failed: Úxai_discovery_failedéÈ   z#xAI OIDC discovery returned status rq   z*xAI OIDC discovery returned invalid JSON: Úxai_discovery_invalid_jsonz2xAI OIDC discovery response was not a JSON object.Úxai_discovery_incomplete)Úauthorization_endpointÚtoken_endpointz;xAI OIDC discovery response was missing required endpoints.©rM   )r   r'   r   rZ   r   Ústatus_coderz   r%   r&   r    r5   ÚvaluesÚitemsr¡   )Útimeout_secondsÚresponser`   ÚpayloadÚkÚ	endpointsrM   rš   s           r   Ú_xai_oauth_discoveryr½     sK  € ð]Ü—9‘9Ô4¸xÐI[Ð>\ÐfuÔvˆð ×Ñ˜sÒ"ÜÐ<¸X×=QÑ=QÐ<RÐRSÐTÐVlÓmÐmðrØ—-‘-“/ˆô �gœtÔ$ÜÐKÐMgÓhÐhØ4`Öa¨q�”F˜7Ÿ;™; q›>Ó*Ñ*Ða€IÐaÜˆy×ÑÓ!Ô"ÜÐTÐVpÓqÐqØ—o‘oÓ'ò 7‰
ˆˆsÜ$ S°Ö6ð7àÐøô ò ]ÜÐ4°S°EÐ:Ð<RÓSÐY\Ð\ûð]ûô ò rÜÐCÀCÀ5ÐIÐKgÓhÐnqÐqûðrüò bs5   ‚C* Á
D Á:"D,Ã*	DÃ3DÄDÄ	D)ÄD$Ä$D)c           	     óô   — |t        | j                  d«      xs |«      j                  «       t        | j                  d«      «      | j                  d«      t        | j                  d«      xs d«      xs ddœS )z^Token block persisted for xAI OAuth; falls back to *fallback_refresh* when none is rotated in.r$   Úid_tokenÚ
expires_inr/   r0   )r#   r$   r¿   rÀ   r/   )r   r'   r   r    )rº   r#   Úfallback_refreshs      r   Ú_xai_tokens_from_payloadrÂ   "  sh   € ð %Ü˜WŸ[™[¨Ó9ÒMÐ=MÓN×TÑTÓVÜ˜7Ÿ;™; zÓ2Ó3À7Ç;Á;È|ÓC\Ü˜WŸ[™[¨Ó6ÒB¸(ÓCÒOÀxñ	ð r!   ç      4@©r³   r¸   c          	     óÌ  — ddl m}m}m} ~  ||«      st	        dt
        › �dd¬«      ‚|j                  «       xs  ||«      d   }t        |d¬«       t        j                  t        d	t        |«      «      «      }t        j                  |d
di¬«      5 }	|	j                  |ddidt        |dœ¬«      }
d d d «       
j                  dk7  rc|
j                   j                  «       }|rd|› �nd}|
j                  dk(  rt	        d|z   dz   dd¬«      ‚t	        d|z   d|
j                  dv ¬«      ‚t#        |
dddddd ¬!«      \  }}i t%        |||«      ¥d" |«       i¥S # 1 sw Y   Œ¨xY w)#Nr   )Ú_nonempty_strrk   r½   z$xAI OAuth is missing refresh_token. Úxai_auth_missing_refresh_tokenTrE   r³   r´   ç      @rª   r«   ©r­   r¬   zContent-Typez!application/x-www-form-urlencodedr$   )Ú
grant_typeÚ	client_idr$   ©r¬   Údatar¯   ú Response: r   i“  z'xAI token refresh failed with HTTP 403.uP   This OAuth account is not authorized for xAI API access â€” xAI may be restricting API/OAuth use to specific SuperGrok tiers despite the in-app subscription being active. Re-logging in won't change that; set ``XAI_API_KEY`` and switch to ``provider: xai`` (API-key path) if available, or upgrade your subscription at https://x.ai/grok.Úxai_oauth_tier_deniedFzxAI token refresh failed.Úxai_refresh_failed>   é�  é‘  r,   )z.xAI token refresh returned invalid JSON: {exc}Úxai_refresh_invalid_json)z1xAI token refresh response was not a JSON object.Úxai_refresh_invalid_response)z4xAI token refresh response was missing access_token.Ú xai_refresh_missing_access_token)ÚproviderÚinvalid_jsonÚinvalid_json_reloginÚ
strict_strÚinvalid_responseÚmissing_accessr1   )r4   rÆ   rk   r½   r   rK   r   r¡   r   ÚTimeoutr‡   r�   ÚClientÚpostr   rµ   Útextr   rÂ   )r#   r$   r³   r¸   rÆ   rk   r½   Úendpointr­   Úclientr¹   ÚdetailÚsuffixrº   Úrefreshed_accesss                  r   Úrefresh_xai_oauth_purerå   ,  s¯  € ÷ PÑOØÙ˜Ô'ÜØ2´8°*Ð=Ð?_Ðimô
ð 	
ð ×#Ñ#Ó%Ò`Ñ)=¸oÓ)NÐO_Ñ)`€Hô ! Ð1AÕBÜ�m‰mœC ¤U¨?Ó%;Ó<Ó=€GÜ	�‰˜g°Ð:LÐ/MÔ	Nð 
ÐRXØ—;‘;Ø˜~Ð/RÐSØ /Ô>QÐdqÑrð ó 
ˆ÷
ð
 ×Ñ˜sÒ"Ø—‘×$Ñ$Ó&ˆÙ+1�;˜v˜hÑ'°rˆð ×Ñ 3Ò&ÜØ9¸FÑBðEñEð (°ô
ð 
ô Ø'¨&Ñ0Ð2FÐPX×PdÑPdÐhrÐPrô
ð 	
ô !>Ø˜;ØcØ"¨uØnØsô!Ñ€GÐð pÔ& wÐ0@À-ÓPÐoÐR`ÑblÓbnÑoÐo÷I
ð 
ús   ÂEÅE#)rJ   c               ó  — ddl m}m}m} 	  | |«       d«      xs i }t	        |j                  d«      xs d«      } |t        | j                  d«      «      t        | j                  d«      «      ||¬«      }	t        | «      }
|	d   |
d<   |	d   |
d<   |	j                  d	«      r|	d	   |
d	<   |	j                  d
«      �|	d
   |
d
<   |	j                  d«      r|	d   |
d<   t        |
d|i||	d   |d¬«       |
S # t        $ r d}Y ŒÇw xY w)Nr   )rf   r+   rå   r,   r2   rb   r#   r$   rÄ   r¿   rÀ   r/   r³   r1   Frc   )
r4   rf   r+   rå   r   r'   rZ   r    r&   ro   )r(   r³   rJ   r¸   rf   r+   rå   r9   r2   Ú	refreshedÚupdated_tokenss              r   Ú_refresh_xai_oauth_tokensré   b  s.  € ÷ _Ñ^ð(Ù$Ñ%5Ó%7¸ÓEÒKÈˆÜ˜Ÿ	™	 +Ó.ÒEÐ2EÓFˆ	ñ 'Üˆv�z‰z˜.Ó)Ó*¬F°6·:±:¸oÓ3NÓ,OØ%°ô€Iô ˜&“\€NØ%.¨~Ñ%>€N�>Ñ"Ø&/°Ñ&@€N�?Ñ#Ø‡}�}�ZÔ Ø%.¨zÑ%:ˆ�zÑ"Ø‡}�}�\Ó"Ð.Ø'0°Ñ'>ˆ�|Ñ$Ø‡}�}�\Ô"Ø'0°Ñ'>ˆ�|Ñ$äØÐ#3°^Ð"DÐS_Ø˜~Ñ.¸)ÐPUõð Ðøô) ò (Ø'Š	ð(ús   Œ0C6 Ã6DÄDc                óv  — ddl m}m}m}m}m} 	  |«       } ||d«      xs i }t        |j                  d«      xs i «      }|j                  dd«       |j                  dd«       ||d<    |d| dd	¬
«      |d<    ||d|d¬«        ||«       y# t        $ r }	t        j                  d|	«       Y d}	~	yd}	~	ww xY w)zÈClear dead xAI tokens after a terminal (400/401/403) refresh failure so later sessions fail fast.

    Best-effort: persistence failures are logged and swallowed; the caller re-raises regardless.
    r   )Ú_last_auth_error_markerrf   r+   ri   rj   r,   r(   r#   Nr$   Úruntime_refresh_failurerÐ   )ÚreasonÚdefault_codeÚlast_auth_errorFrU   z2xAI OAuth: failed to persist quarantined state: %s)r4   rë   rf   r+   ri   rj   r&   r'   ÚpoprZ   r[   r\   )
r`   rë   rf   r+   ri   rj   Ústorer9   r(   Úsave_excs
             r   Ú_quarantine_xai_oauth_tokensró   �  sÃ   € ÷
 Iõ  IðUÙ Ó"ˆÙ$ U¨KÓ8Ò>¸BˆÜ�e—i‘i Ó)Ò/¨RÓ0ˆØ�
‰
�> 4Ô(Ø�
‰
�? DÔ)ð !ˆˆh‰Ù#:Ø˜Ð%>ÐMaô$
ˆÐÑ ñ 	˜e [°%ÀEÕJÙ˜ÕøÜò UÜ�‰ÐIÈ8×TÑTûðUús   �A>B Â	B8ÂB3Â3B8c                 óð   — t        t        j                  dd«      j                  «       j	                  d«      xs3 t        j                  dd«      j                  «       j	                  d«      t
        ¬«      S )NÚHERMES_XAI_BASE_URLr   r£   ÚXAI_BASE_URL)r¦   )r¨   rW   Úgetenvr   r¤   r   r.   r!   r   Ú_xai_oauth_inference_base_urlrø   š  s[   € Ü+Ü
�	‰	Ð'¨Ó,×2Ñ2Ó4×;Ñ;¸CÓ@ÒuÄBÇIÁIÈnÐ^`ÓDa×DgÑDgÓDi×DpÑDpÐqtÓDuÜ+ôð r!   F)Úforce_refreshÚrefresh_if_expiringÚrefresh_skew_secondsc           
     óÆ  ‡ ‡‡— ddl m}m}m}m} dˆ ˆˆfd„}t        «       }t        |d   «      }	t        dd«      }
 ||«      r¶ |t        t        t        «      |
dz   «      ¬«      5  t        d	¬
«      }t        |d   «      }	 ||«      rht        t        |j                  d«      xs i «      j                  d«      «      xs  ||
«      d   }	  ||	|t        |j                  d«      «      |
¬«      }	d d d «       dt        «       t        |	j                  d«      «      d|j                  d«      ddœS # t        $ r} ||«      rt        |«       ‚ d }~ww xY w# 1 sw Y   ŒfxY w)Nr   )re   Ú$_is_terminal_xai_oauth_refresh_errorré   r½   c                óº   •— t        | d   j                  d«      «      }‰�t        ‰«      n
t        |«      }t	        ‰«      xs t	        ‰xr t        ||«      «      S )Nr(   r#   )r    r'   r€   r�   Úboolr‰   )rÍ   r#   Úskewrù   rú   rû   s      €€€r   Ú_should_refreshz>resolve_xai_oauth_runtime_credentials.<locals>._should_refresh§  sc   ø€ Ü˜d 8™n×0Ñ0°Ó@ÓAˆà)=Ð)IŒCÐ$Ô%Ü4°\ÓBð 	ô �MÓ"ò 
¤dØÒUÔ$AÀ,ÐPTÓ$Uó'
ð 	
r!   r(   Ú"HERMES_XAI_REFRESH_TIMEOUT_SECONDSé   rÈ   )r¸   Fr@   rI   r³   rJ   )r³   rJ   r¸   r,   r#   zhermes-auth-storer1   rb   )rÖ   Úbase_urlÚapi_keyÚsourcer1   r2   )rÍ   úDict[str, Any]Úreturnrÿ   )r4   re   rý   ré   r½   rN   r&   r   r‡   r�   r   r    r'   r   ró   rø   )rù   rú   rû   re   rý   ré   r½   r  rÍ   r(   Úrefresh_timeout_secondsr³   r`   s   ```          r   Ú%resolve_xai_oauth_runtime_credentialsr
  ¡  s\  ú€ ÷ Hó  H÷
ô "Ó#€DÜ�$�x‘.Ó!€FÜ'Ð(LÈbÓQÐÙ�tÔÙ¬c´%Ô8QÓ2RÐTkÐnqÑTqÓ.rÔsñ 	ä)°Ô6ˆDÜ˜$˜x™.Ó)ˆFÙ˜tÔ$äœ4 §¡¨Ó 5Ò ;¸Ó<×@Ñ@ÐAQÓRÓSò WÙ+Ð,CÓDÐEUÑVð ðÙ6Ø¨~ÌFÐSW×S[ÑS[Ð\jÓSkÓLlØ(?ô�F÷	ð(  Ü1Ó3Ü˜&Ÿ*™* ^Ó4Ó5Ø%ØŸ™ Ó0à(ñð øô !ò Ù;¸CÔ@Ü4°SÔ9Øûðú÷	ð 	ús+   Á'A&EÃ$D2Ä2	EÄ;EÅEÅEÅE )Úforce_new_loginc               ó  — ddl m}m}m}m}m}m}m}	 ~|s |d|t        dt        ¬«      ry t        «        t        d«       t        d«       t        «        t        t        | dd «      xs d	«      }
t        | d
d«       } |«       rd} ||
|¬«      }t        |d   |j                  d«      |j                  dd«      |j                  d«      d¬«        |	dd«        |d|j                  dt        «      «      } |d|d¬«       y )Nr   )Ú_is_remote_sessionÚ!_offer_existing_oauth_credentialsÚ_print_login_successÚ_update_config_for_providerÚ_xai_oauth_device_code_loginr
  Úunsuppress_credential_sourcer,   z	xAI OAuth)rY   Úis_expiringÚdisplay_nameÚdefault_base_urlz6Signing in to xAI Grok OAuth (SuperGrok / Premium+)...z,(Hermes creates its own local OAuth session)r­   rÃ   Ú
no_browserF©r¸   Úopen_browserr(   rI   rJ   r   r1   rb   )rI   rJ   r1   r2   Údevice_coder  T)Úshow_auth_state)r4   r  r  r  r  r  r
  r  r‰   r   Úprintr�   Úgetattrro   r'   )ÚargsÚpconfigr  r  r  r  r  r  r
  r  r¸   r  ÚcredsÚconfig_paths                 r   Ú_login_xai_oauthr!  Ó  sù   € ÷ l÷  lñ  lØáÑ@ØØ5Ü1Ø Ü3õ ð 	ä	„GÜ	Ð
BÔCÜ	Ð
8Ô9Ü	„GäœG D¨)°TÓ:ÒB¸dÓC€OÜ˜t \°5Ó9Ð9€LÙÔØˆá(¸ÐWcÔd€EÜØˆh‰ 5§9¡9¨[Ó#9Ø—Y‘Y˜~¨rÓ2ÀÇÁÈ>ÓAZØ%õñ ! ¨mÔ<Ù-¨k¸5¿9¹9ÀZÔQkÓ;lÓm€KÙ˜ kÀ4ÖHr!   )Úscopec               óš  — | j                  t        t        t        |dœ¬«      }|j                  dk7  rMt        d|j                  › d�|j                  r$d|j                  j                  «       › �z   d«      ‚dz   d«      ‚|j                  «       }d	}|D �cg c]	  }||vsŒ|‘Œ }}|rt        d
dj                  |«      › �d«      ‚|S c c}w )N)rË   r"  rÌ   r¯   z%xAI device-code request failed (HTTP z).rÎ   r   Údevice_code_request_failed)r  Ú	user_codeÚverification_uriÚverification_uri_completerÀ   Úintervalz)xAI device-code response missing fields: z, Údevice_code_invalid)
rÞ   r   r   r   rµ   r   rß   r   rz   Újoin)rá   r"  r¹   rº   ÚrequiredÚkeyÚmissings          r   Ú_xai_oauth_request_device_coder.  ø  sê   € Ø�{‰{Ü!Ô+=ÔReÐpuÑDvð ó €Hð ×Ñ˜sÒ"ÜØ3°H×4HÑ4HÐ3IÈÐLØ8@¿º�˜XŸ]™]×0Ñ0Ó2Ð3Ð4ñOà(ó
ð 	
àKMñOà(ó
ð 	
ð
 �m‰m‹o€GØv€HØ&Ö=�s¨#°WÒ*<ŠsÐ=€GÐ=ÙÜÐBÀ4Ç9Á9ÈWÓCUÐBVÐWÐYnÓoÐoØ€Nùò >s   Â	CÂ Cc          
     óˆ   ‡ ‡‡— ddl m} d
d„}dd„} |ˆ ˆˆfd„t        |«      t        dt        |«      «      |d„ |d„ ¬	«      S )Nr   )Ú_poll_device_token_genericc                ó`   — dD ])  \  }}| j                  |«      rŒt        d|› d|› d�d«      ‚ y )N))r#   Úan)r$   Úaz/xAI device-code token response did not include ú rq   Úxai_device_token_invalid)r'   r   )rº   Ú
field_nameÚarticles      r   Ú	_validatez/_xai_oauth_poll_device_token.<locals>._validate  sF   € Ø#Sò 	ÑˆJ˜Ø—;‘;˜zÕ*ÜØEÀgÀYÈaÐPZÈ|Ð[\Ð]Ø.óð ñ	r!   c                ó„   — |j                  d«      xs |j                  d«      xs | j                  }t        d|› �d«      S )NÚerror_descriptionÚerrorz&xAI device-code token polling failed: Úxai_device_token_failed)r'   rß   r   )r¹   Úerror_payloadÚdescriptions      r   Ú_errorz,_xai_oauth_poll_device_token.<locals>._error  sF   € Ø#×'Ñ'Ð(;Ó<ÒkÀ×@QÑ@QÐRYÓ@ZÒkÐ^f×^kÑ^kˆÜÐ@ÀÀÐNÐPiÓjÐjr!   c                 óL   •— ‰ j                  ‰t        t        t        ‰dœ¬«      S )N)rÊ   rË   r  rÌ   )rÞ   r   r   r   )rá   r  r³   s   €€€r   ú<lambda>z._xai_oauth_poll_device_token.<locals>.<lambda>  s'   ø€ �—‘ØÔ$6Ü 6ÔEXÐitÑuð ó 
€ r!   rs   c                ó   — t        dd«      S )NzAxAI device-code token polling returned a non-JSON error response.r<  ©r   )Ú_rs    r   rA  z._xai_oauth_poll_device_token.<locals>.<lambda>#  s   € ¤XØOÐQjó&
€ r!   c                 ó   — t        dd«      S )Nz/Timed out waiting for xAI device authorization.Údevice_code_timeoutrC  r.   r!   r   rA  z._xai_oauth_poll_device_token.<locals>.<lambda>'  s   € œ8Ð$UÐWlÓm€ r!   )rÀ   Úpoll_intervalÚvalidate_successÚon_non_json_errorÚon_errorÚ
on_timeout)rº   r  r  ÚNone)r  rZ   )r4   r0  r€   r‡   )rá   r³   r  rÀ   rG  r0  r8  r?  s   ```     r   Ú_xai_oauth_poll_device_tokenrM  
  sO   ú€ õ ;óókñ &õ	
ô �z“?Ü˜!œS Ó/Ó0Ø"ñ
ð Ùmôð r!   r  c           
     ó’  — ddl m}m}m}m}m}m}  || «      }t        j                  t        d| «      «      }	t        j                  |	ddi¬«      5 }
t        |
«      }t        |d   «      } |t        |j                  d«      xs |d	   «      t        |d
   «      |xr  |«        xr  |«       d¬«       t        dt        d|«      › d�«        ||
|d   t        |d   «      t        |d   «      |¬«      }d d d «       t!        «      \  }}|r|st#        dd«      ‚t%        |||«      |dt'        «        |«       ddœS # 1 sw Y   ŒHxY w)Nr   )Ú_can_open_graphical_browserr  Ú_print_device_code_instructionsrk   r½   rM  rÃ   rª   r«   rÉ   r(  r'  r&  r%  T)r  Úswallow_open_errorsz$Waiting for approval (polling every rs   zs)...r³   r  rÀ   )r³   r  rÀ   rG  z;xAI device-code token response was missing required tokens.r5  r   zoauth-device-code)r(   rI   rJ   r  r1   r  )r4   rO  r  rP  rk   r½   rM  r   rÜ   r‡   rÝ   r.  r€   r   r'   r  r)   r   rÂ   rø   )r¸   r  rO  r  rP  rk   r½   rM  rI   r­   rá   Údevice_datar(  rº   r#   r$   s                   r   r  r  +  s[  € ÷ q÷  qÙ$ _Ó5€IÜ�m‰mœC  oÓ6Ó7€GÜ	�‰˜g°Ð:LÐ/MÔ	Nð 
ÐRXÜ4°VÓ<ˆÜ�{ :Ñ.Ó/ˆÙ'Ü�—‘Ð ;Ó<Ò_ÀÐL^Ñ@_Ó`Ü�˜KÑ(Ó)Ø%ÒdÑ.@Ó.BÐ*BÒdÑGbÓGdØ $õ		
ô 	Ð4´S¸¸HÓ5EÐ4FÀeÐLÔMÙ.Ø 9Ð-=Ñ#>Ü˜K¨Ñ6Ó7ÄCÈÐT`ÑHaÓDbØ"ô
ˆ÷
ô  #.¨gÓ"6Ñ€L�-Ù™}ÜÐTÐVpÓqÐqä*¨7°LÀ-ÓPØ°Ô@]Ó@_Ù"›Ð0Cñð ÷'
ð 
ús   ÁB%D=Ä=E)r   r   r  r   )r(   r   r  ztuple[str, str])r8   r  r  úOptional[Dict[str, Any]])r9   rS  r  rÿ   )rA   rÿ   r  r  )r9   r  r  rL  )r(   r  rI   rS  rJ   r   r1   zOptional[str]r2   r   rV   rÿ   r  rL  )r#   r   r  zOptional[float])r   )r#   r   rˆ   r€   r  rÿ   )r#   r   r  r€   )r’   r   r  rÿ   )rš   r   r  ztuple[Optional[str], str])rš   r   rM   r   r  r   )r   r   r¦   r   r  r   )g      .@)r¸   r�   r  zDict[str, str])rº   r  r#   r   rÁ   r   r  r  )
r#   r   r$   r   r³   r   r¸   r�   r  r  )
r(   r  r³   r   rJ   r   r¸   r�   r  r  )r`   r   r  rL  )r  r   )rù   rÿ   rú   rÿ   rû   zOptional[int]r  r  )r  r   r  rÿ   r  rL  )rá   úhttpx.Clientr"  r   r  r  )rá   rT  r³   r   r  r   rÀ   r€   rG  r€   r  r  )r¸   r�   r  rÿ   r  r  )AÚ__doc__Ú
__future__r   Úloggingr|   rz   rW   r†   Úpathlibr   Útypingr   r   r   r   Úurllib.parser	   Úhermes_cli.auth_codexr
   r   Úhermes_cli.auth_constantsr   r   r   r   r   r   r   r   r   r   r   r   Úutilsr   r4   r   Ú	getLoggerr[   rK   r    r)   r=   r?   rN   ra   ro   r„   r‰   r�   r“   rœ   r¡   r¨   r½   rÂ   rå   ré   ró   rø   r
  r!  r.  rM  r  r.   r!   r   ú<module>r_     s   ðñõ #ã Û Û Û 	Û Ý ß 5Ó 5Ý !ß ^÷÷ ÷ ó õ
 áÝ.Ø	ˆ×	Ñ	Ð,Ó	-€à1€ó$óSó6ó4Mð -1õ ó6Pð8 FJÐ_aØ"&Ð9LØñ)Øð)Ø*Bð)ØY\ð)àð)à36ð)ð ð)ð 
ó	)óDôNó
Hó4ó

ó5ó,ôDó*ð EGØ!ñ3pØð3pØ&)ð3pØ>Að3pàð3pð ó3pðn IKñØðØ/2ðØBEðØ]bðàóó>Uó2ð #ÀØ*.ñ/Øð/Ø9=ð/à'ð/ð ó/ðd PUõ "IðJ JYõ ð$ØðØ-0ðØ?BðØPSðàðð óðB >BÐX\ö r!   