+
    jHàjSë  ã                  óæ  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
Ht ^ RIHt ^ RIHtHtHtHtHtHtHt ^ RIHtHtHtHtHtHtHtHtHtH t H!t! ^ RI"H#t#H$t$ ]'       d   ^ RI%H&t& ]PN                  ! R	4      t(R
t)Rt*Rt+R R lt,R R lt-R R lt.R R lt/R R lt0R R lt1R{R R llt2RR/R R llt3R R  lt4R!R"/R# R$ llt5R|R% R& llt6R}R'R"R(R)/R* R+ lllt7R|R, R- llt8R.R"R/RR0R"/R1 R2 llt9R~t:R3 R4 lt;R5 R6 lt<R7 R8 lt=Rt>R9 R: lt?R; R< lt@R= R> ltAR? R@ ltBRA RB ltCRCRD/RE RF lltDRG RH ltERI RJ ltFRKR)RLR"RM]RNR)/RO RP lltGRQ RR ltHRS RT ltIRUtJ/ tKRV]LRW&   ]Pš                  ! 4       tNRX RY ltORZ R[ ltPRRR\]J/R] R^ lltQR\]J/R_ R` lltRRa Rb ltSR|Rc Rd lltTRe Rf ltURg Rh ltVRi Rj ltWRk Rl ltXRmR)/Rn Ro lltYRpR/Rq Rr lltZRs Rt lt[Ru Rv lt\Rw Rx lt]Ry Rz lt^R# )€aÍ  OpenAI Codex OAuth: token store, refresh, quota probe, device-code login.

Tokens live in ~/.hermes/auth.json, NOT ~/.codex/: Hermes keeps its own Codex OAuth session
separate from the Codex CLI / VS Code extension so one app's refresh-token rotation cannot
invalidate the other's session.

Split out of ``hermes_cli/auth.py``; origin helpers are imported lazily inside each function
so ``hermes_cli.auth.<name>`` patches still intercept (and no import cycle).
)ÚannotationsN)Úsuppress)ÚPath)ÚTYPE_CHECKINGÚAnyÚDictÚIteratorÚListÚOptionalÚTuple)Ú_decode_jwt_claimsÚAUTH_LOCK_TIMEOUT_SECONDSÚ	AuthErrorÚ'CODEX_ACCESS_TOKEN_REFRESH_SKEW_SECONDSÚCODEX_OAUTH_CLIENT_IDÚCODEX_OAUTH_TOKEN_URLÚCODEX_OAUTH_USER_AGENTÚCODEX_RATE_LIMITED_CODEÚDEFAULT_CODEX_BASE_URLÚ
_codex_errÚhttpx)Ú	env_floatÚnormalize_proxy_env_vars)ÚProviderConfigzhermes_cli.authzGCodex auth is missing access_token. Run `{relogin}` to re-authenticate.zHCodex auth is missing refresh_token. Run `{relogin}` to re-authenticate.z=No Codex credentials stored. Run `{relogin}` to authenticate.c               ó   € V ^8„  d   QhRR/# ©é   ÚreturnÚstr© )Úformats   "Ú:/home/ubuntu/.hermes/hermes-agent/hermes_cli/auth_codex.pyÚ__annotate__r"   )   s   € ÷ 1ñ 1 ñ 1ó    c                 ó   € ^ RI Hp  V ! R4      # )é    ©Úoauth_relogin_commandúopenai-codex)Úagent.turn_failure_copyr'   r&   s    r!   Ú_codex_relogin_commandr*   )   s   € Ý=á  Ó0Ð0r#   c               ó    € V ^8„  d   QhRRRR/# )r   Úheadersr   r   úOptional[int]r   )r    s   "r!   r"   r"   /   s   € ÷ 5ñ 5¨ð 5°ñ 5r#   c                ó@   € ^ RI Hp V! V 4      pVf   R# \        V4      # )zABest-effort parse of a ``Retry-After`` header into whole seconds.)Úparse_retry_after_secondsN)Úagent.retry_utilsr/   Úint)r,   r/   Úsecondss   &  r!   Ú_parse_retry_after_secondsr3   /   s"   € å;Ù'¨Ó0€GØ’?ˆ4Ð4¬¨G«Ð4r#   c               ó    € V ^8„  d   QhRRRR/# )r   Úvaluer   r   r   r   )r    s   "r!   r"   r"   6   s   € ÷ $ñ $�Sð $˜Sñ $r#   c                óH   € \        T ;'       g    R 4      P                  4       # ©Ú )r   Ústrip)r5   s   &r!   Ú	_strippedr:   6   s   € Üˆu�{ˆ{˜Ó×!Ñ!Ó#Ð#r#   c               ó    € V ^8„  d   QhRRRR/# )r   Úentryúdict[str, Any]r   ÚNoner   )r    s   "r!   r"   r"   :   s   € ÷ #ñ # Nð #°tñ #r#   c                ó,   € ^ RI Hp V F  pRW&   K	  	  R# )z?Reset a pool entry's cooldown / last-error metadata to healthy.)Ú_POOL_STATUS_FIELDSN)Úhermes_cli.authr@   )r<   r@   Ústatus_fields   &  r!   Ú_clear_pool_entry_statusrC   :   s   € å3Û+ˆØ"ˆÓó ,r#   c               ó$   € V ^8„  d   QhRRRRRR/# )r   Úaccess_tokenr   Úskew_secondsr1   r   Úboolr   )r    s   "r!   r"   r"   A   s&   € ÷ eñ e°#ð eÀSð eÈTñ er#   c                óè   € \        V 4      P                  R 4      p\        V\        \        34      ;'       d<    \	        V4      \
        P
                  ! 4       \        ^ \        V4      4      ,           8*  # )Úexp)r   ÚgetÚ
isinstancer1   ÚfloatÚtimeÚmax)rE   rF   rI   s   && r!   Ú_codex_access_token_is_expiringrO   A   sR   € Ü
˜\Ó
*×
.Ñ
.¨uÓ
5€CÜ�cœC¤˜<Ó(×dÐd¬U°3«Z¼D¿IºI»KÌ#ÈaÔQTÐUaÓQbÓJcÕ<cÑ-dÐdr#   c               ó   € V ^8„  d   QhRR/# r   r   )r    s   "r!   r"   r"   F   s   € ÷ `ñ `˜ñ `r#   c                 ó†   € \         P                  ! R R4      P                  4       P                  R4      ;'       g    \        # )ÚHERMES_CODEX_BASE_URLr8   Ú/)ÚosÚgetenvr9   Úrstripr   r   r#   r!   Ú_codex_base_urlrW   F   s1   € Ü�9Š9Ð,¨bÓ1×7Ñ7Ó9×@Ñ@ÀÓE×_Ð_ÔI_Ð_r#   r8   c               ó    € V ^8„  d   QhRRRR/# )r   Úentry_base_urlúOptional[str]r   r   r   )r    s   "r!   r"   r"   J   s   € ÷ .ñ .¨}ð .Àcñ .r#   c           	     ó  € \        V 4      P                  R4      p ^ RIHp ^ RIHp V! 4       P                  R4      pT! RR\        V\        4      '       d   TM/ RV4      ^,          #   \         d�    \        P                  RR	R
7       \        \        4      ;_uu_ 4        ^ RIHp \        T! RR4      4      P                  R4      ;'       g    TpRRR4       M  + '       g   i     M; iT;'       g    \        u # i ; i)uo  Base URL the chat route sends a pooled Codex credential to â€” the same rule
``runtime_provider._pool_entry_mode_and_url`` applies (``HERMES_CODEX_BASE_URL`` > ``model.base_url``
while the row still carries the canonical URL > the row's own URL). A pooled gateway key belongs to
that host only; composing it with the ambient default sends it to chatgpt.com (#121486).rS   )Úload_config_readonly)Ú_pool_entry_mode_and_urlÚmodelr(   Nr8   z'Codex pool route base resolution failedT©Úexc_info)Úget_secret_strrR   )r:   rV   Úhermes_cli.configr\   Úhermes_cli.runtime_providerr]   rJ   rK   ÚdictÚ	ExceptionÚloggerÚdebugr   Úagent.secret_scopera   r   )rY   Úbaser\   r]   Ú	model_cfgra   s   &     r!   Ú_codex_pool_route_base_urlrk   J   sà   € ô
 �^Ó$×+Ñ+¨CÓ0€Dð.Ý:ÝHÙ(Ó*×.Ñ.¨wÓ7ˆ	Ù'Ø˜D¬z¸)ÄT×/JÒ/J¡)ÐPRÐTVÐX\ó^Ø^_õað 	aøäô .Ü�‰Ð>ÈˆÔNä”i× Õ Ý9Ü™^Ð,CÀRÓHÓI×PÑPÐQTÓU×]Ð]ÐY]ˆD÷ !× × Ð úð ×-Ð-Ô-Ò-ð.ús0   œAA) Á)9DÂ"2CÃ	DÃC-Ã(DÃ9DÄDÚbase_urlc          
     ó,   € V ^8„  d   QhRRRRRRRRRR/# )	r   Úapi_keyr   ÚsourceÚlast_refreshrZ   rl   r   r=   r   )r    s   "r!   r"   r"   _   s;   € ÷ Pñ PØðPØ ðPØ0=ðPØIVðPØbpñPr#   c               óB   € R RRT;'       g    \        4       RV RVRVRR/# )Úproviderr(   rl   rn   ro   rp   Ú	auth_modeÚchatgpt)rW   )rn   ro   rp   rl   s   &$$$r!   Ú_codex_runtime_resultru   _   s6   € ð 	�N J°×0MÐ0M¼OÓ<MÈyÐZaØ�&˜.¨,¸ÀYðPð Pr#   c               ó    € V ^8„  d   QhRRRR/# )r   ÚlockrG   r   r=   r   )r    s   "r!   r"   r"   f   s   € ÷ ñ ¨ð °ñ r#   c                ó¤   € ^ RI HpHp V '       d"   V! 4       ;_uu_ 4        V! 4       uuRRR4       # V! 4       #   + '       g   i     T! 4       # ; i)zVLoad the auth store, taking the cross-process lock unless the caller already holds it.)Ú_auth_store_lockÚ_load_auth_storeN)rA   ry   rz   )rw   ry   rz   s   &  r!   Ú_load_auth_store_maybe_lockedr{   f   s:   € çBßÙ×ÕÙ#Ó%÷  ÒáÓÐ÷  ÖáÓÐús	    9¹A	Ú_lockTc               ó    € V ^8„  d   QhRRRR/# )r   r|   rG   r   r=   r   )r    s   "r!   r"   r"   o   s   € ÷ Iñ I ð I°ñ Ir#   c                óf  € ^ RI HpHp \        V 4      pV! VR4      pV'       g+   \	        \
        P                  \        4       R7      RRR7      hVP                  R4      p\        V\        4      '       g   \	        R\        4        R2R	RR7      hV! VP                  R
4      4      '       g+   \	        \        P                  \        4       R7      RRR7      hV! VP                  R4      4      '       g+   \	        \        P                  \        4       R7      RRR7      hRVRVP                  R4      /# )zERead Codex OAuth tokens from Hermes auth store (~/.hermes/auth.json).)Ú_load_provider_stateÚ_nonempty_strr(   ©ÚreloginÚcodex_auth_missingTÚtokensz)Codex auth state is missing tokens. Run `ú` to re-authenticate.Úcodex_auth_invalid_shaperE   Úcodex_auth_missing_access_tokenÚrefresh_tokenÚ codex_auth_missing_refresh_tokenrp   )rA   r   r€   r{   r   Ú_NO_CREDENTIALS_MSGr    r*   rJ   rK   rd   Ú_MISSING_ACCESS_TOKEN_MSGÚ_MISSING_REFRESH_TOKEN_MSG)r|   r   r€   Ú
auth_storeÚstater„   s   $     r!   Ú_read_codex_tokensr�   o   s  € çCÜ.¨uÓ5€JÙ  ¨^Ó<€EßÜÔ,×3Ñ3Ô<RÓ<TÐ3ÓUØ-°tô=ð 	=à�Y‰Y�xÓ €FÜ�fœd×#Ò#ÜØ7Ô8NÓ8PÐ7QÐQfÐgØ&°ô6ð 	6ñ ˜Ÿ™ NÓ3×4Ò4ÜÔ2×9Ñ9ÔBXÓBZÐ9Ó[Ø:ÀDôJð 	Já˜Ÿ™ OÓ4×5Ò5ÜÔ3×:Ñ:ÔCYÓC[Ð:Ó\Ø;ÀTôKð 	Kà�f˜n¨e¯i©i¸Ó.GÐHÐHr#   c          
     ó,   € V ^8„  d   QhRRRRRRRRR	R
/# )r   r�   r=   r„   údict[str, str]rp   rZ   Úprevious_singleton_tokensúOptional[dict[str, str]]r   r>   r   )r    s   "r!   r"   r"   …   s4   € ÷ &(ñ &(Øð&(Ø(6ð&(ØFSð&(à7ð&(àCGñ&(r#   c                óê  € VP                  R4      pV'       g   R# VP                  R4      p\        V R4      pVf   R# T;'       g    / P                  R4      ;'       g    Rp\        V4       F€  pVP                  R4      p	V	R8H  ;'       d(    \        T;'       d    VP                  R4      V8H  4      p
V	R8X  g   V
'       g   KY  WHR&   V'       d   WXR&   V'       d   W(R&   \	        V4       K‚  	  R# )	uò  Mirror a fresh Codex re-auth into the credential_pool OAuth entries.

``device_code`` (the singleton-seeded entry from ``hermes setup`` / the model picker) is always
synced. ``manual:device_code`` (``hermes auth add openai-codex``) is synced only when its
access_token equals the PREVIOUS singleton token â€” a legacy alias of the singleton; an entry
with its own token material is an independent account and must be left alone. ``manual:api_key``
and any other source are independent credentials and are never overwritten by a re-auth.

See #33000, #39236.
The original #33538 fix refreshed every ``manual:device_code`` entry unconditionally. That worked when
``manual:device_code`` only meant "legacy alias of the singleton", but the same source string is now
also produced by independent-account additions, and the broad sync silently clobbered distinct accounts
with the latest-authenticated token pair. The access_token-match check distinguishes the two cases
without changing the source-string contract.
rE   Nrˆ   r(   ro   zmanual:device_codeÚdevice_coderp   )rJ   Ú_pool_entriesÚ_codex_pool_dictsrG   rC   )r�   r„   rp   r’   rE   rˆ   ÚentriesÚprev_atr<   ro   Úis_aliass   &&&&       r!   Ú_sync_codex_pool_entriesr›   …   sß   € ð$ —:‘:˜nÓ-€LßÙØ—J‘J˜Ó/€MÜ˜J¨Ó7€GØ‚Ùà(×.Ð.¨B×3Ñ3°NÓC×KÐKÀt€GÜ" 7Ö+ˆØ—‘˜8Ó$ˆØÐ1Ñ1÷ >ð >´dØ×<Ð<˜Ÿ	™	 .Ó1°WÑ<ó7>ˆà˜-Ô'¯8ÙØ ,ˆnÑßØ%2�/Ñ"ßØ$0�.Ñ!Ü  Ö'ó ,r#   Ú
set_activeÚwrite_throughFc               ó0   € V ^8„  d   QhRRRRRRRRRRR	R
/# )r   r„   r‘   rp   z
str | NoneÚlabelrœ   rG   r�   r   r>   r   )r    s   "r!   r"   r"   ®   sF   € ÷ (@ñ (@Øð(@Ø*4ð(@ØDNð(@àð(@à,0ð(@ð 
ñ(@r#   c          	     óŠ  € ^ RI HpHpHpHpHp	Hp
Hp Vf   V! 4       pV! R4      ;_uu_ 4       w  rÍpV'       d   \        V4      M/ p\        VP                  R4      \        4      '       d   VP                  R4      MRpVP                  WRR7       V'       d<   \        V4      P                  4       '       d   \        V4      P                  4       VR&   TRppV'       d$   Ve    V! Wå! 4       4      '       g   V! V4      TRpppV
! VRWÓR	7       \        VWVR
7       V	! VVR7       RRR4       R#   + '       g   i     R# ; i)u‘  Save Codex OAuth tokens to the auth store the grant was resolved FROM.

Codex refresh tokens are single-use with rotation-family reuse detection. A profile without its
own ``providers.openai-codex`` block reads root's grant via the fallback, so a refresh under that
profile must rotate ROOT's chain â€” singleton AND ``credential_pool`` entries â€” or root keeps the
consumed refresh token, the next process replays it and OpenAI revokes the whole family
(#87503). Root-only write-back: a profile copy would shadow root and disable the write-through
on the next refresh (#74339). Mirrors the xAI source-aware save.

Only a token REFRESH passes ``write_through=True``: a fresh login or import under a profile
is the profile's own grant and must not overwrite the root account it was borrowing.
``set_active=False`` stores credentials for a side tool (image gen) without making Codex the
active inference provider.
)Ú_auth_file_pathrz   Ú_provider_state_transactionÚ
_same_pathÚ_save_auth_storeÚ_store_provider_stateÚ
_utc_now_zNr(   r„   rt   )r„   rp   rs   rŸ   F)rœ   )r’   ©Útarget_path)rA   r¡   rz   r¢   r£   r¤   r¥   r¦   rd   rK   rJ   Úupdater   r9   r›   )r„   rp   rŸ   rœ   r�   r¡   rz   r¢   r£   r¤   r¥   r¦   r�   rŽ   Úsource_pathr’   Útarget_storer¨   s   &&&$$             r!   Ú_save_codex_tokensr¬   ®   s  € ÷$=÷ =ñ =ð ÒÙ!“|ˆÙ	$ ^×	4Ô	4Ñ8X¸ÈKß$”�U”¨"ˆô $.¨e¯i©i¸Ó.AÄ4×#HÒ#HˆE�I‰I�hÔÈdð 	"à�‰˜FÈˆÔSß”S˜“Z×%Ñ%×'Ò'Ü  ›Z×-Ñ-Ó/ˆE�'‰NØ$.°�kˆß˜[Ò4¹ZÈÐUdÓUf×=gÒ=gñ 5EÀ[Ó4QÐS^Ð`e z˜+ˆLÙ˜l¨N¸EÕYÜ Ø˜&ÐJcõ	eá˜°;Õ?÷# 
5×	4×	4Ò	4ús   ®C9D1Ä1E	c               ó$   € V ^8„  d   QhRRRRRR/# )r   Úreasonr   Úobserved_access_tokenrZ   r   r“   r   )r    s   "r!   r"   r"   Ù   s$   € ÷ 'ñ 'Øð'Ø,9ð'ØE]ñ'r#   c                óf  € ^ RI Hp ^ RIHp ^ RIHpHpHp V! 4       '       g   R# V! 4       pV'       dA   \        VP                  R4      4      '       d!   \        VP                  R4      4      '       g   R# \        V4      ;'       g    RpV! R4      ;_uu_ 4       w  ršpT
;'       g    / P                  R4      p\        V\        4      '       d   TM/ p\        VP                  R4      4      ;'       g    RV8w  d"   \        P                  R	V 4        RRR4       R# V! V4      pV'       dA   V! VR,          4      RV39  d+   \        P                  R
V \        4       4        RRR4       R# \        P                  RV 4       V! V4       RRR4       \        V4      #   + '       g   i     L; i)a[  Adopt a valid Codex CLI token pair into Hermes auth, if available.

Automatic adoption only; the interactive import offer in ``_login_openai_codex`` asks first and is
not subject to ``auth.adopt_external_logins``.

``observed_access_token`` is the singleton access_token (or None) the caller saw when it decided
the credential needs repair. Recovery repairs THAT credential and nothing else (#73667): a Codex
Desktop/CLI login into another ChatGPT workspace must not replace it silently, and a concurrent
explicit re-auth must not be overwritten, so the save is a compare-and-swap under the store lock.
)Ú_codex_principal_identity)Úadopt_external_logins_enabled)Ú_import_codex_cli_tokensr¢   r¬   NrE   rˆ   r(   r„   zOCodex CLI recovery skipped (%s): the credential was re-authenticated meanwhile.z™Codex CLI recovery refused (%s): the Codex CLI login belongs to a different ChatGPT workspace than the Hermes credential. Run `%s` to re-authenticate it.z3Codex auth recovered from Codex CLI auth.json (%s).)Úagent.credential_poolr±   Úagent.credential_sourcesr²   rA   r³   r¢   r¬   r:   rJ   rK   rd   rf   ÚinfoÚwarningr*   )r®   r¯   r±   r²   r³   r¢   r¬   ÚimportedÚobservedÚ_storerŽ   Ú_sourceÚstoredÚknowns   &&            r!   Ú_recover_codex_tokens_from_clir¾   Ù   s^  € õ @ÝFßiÑiÙ(×*Ò*ÙÙ'Ó)€H÷ œ 8§<¡<°Ó#?×@Ò@Ü˜(Ÿ,™, Ó7×8Ò8ÙÜÐ.Ó/×7Ð7°4€HÙ	$ ^×	4Ô	4Ñ8P¸ÈØ—+�+˜2×"Ñ" 8Ó,ˆÜ% f¬d×3Ò3‘¸ˆÜ�f—j‘j Ó0Ó1×9Ð9°T¸hÔFÜ�K‰KÐiÐkqÔrØ÷ 
5Ñ	4ñ *¨(Ó3ˆßÑ.¨x¸Õ/GÓHÐQUÐW\ÐP]Ô]Ü�N‰NðXàÔ.Ó0ô2ð ÷ 
5Ñ	4ô 	�‰ÐIÈ6ÔRÙ˜8Ô$÷ 
5ô �‹>Ð÷ 
5×	4ús%   ÂAF Ã6F ÄF Ä/6F Å/F Æ F0	Úrelogin_requiredÚinvalid_json_reloginÚ
strict_strc               ó<   € V ^8„  d   QhRRRRRRRRR	RR
RRRRRRR/	# )r   Úresponseúhttpx.Responserr   r   Úinvalid_jsonútuple[str, str]Úinvalid_responsezOptional[tuple[str, str]]Úmissing_accessr¿   rG   rÀ   úOptional[bool]rÁ   r   ztuple[dict[str, Any], str]r   )r    s   "r!   r"   r"     s]   € ÷ ñ ØðØ+.ðØ>Mðà/ðàAPðð ðð :Hðð ð	ð !;ñ	r#   c               óÆ  a€ V3R V3R lllp V P                  4       p	\        T	\        4      '       g   Te   T! T!  h/ p	T	P                  R4      pT'       d*   \        T\        4      '       d   TP                  4       MRpM\        T4      pT'       g   T! T!  hYœ3#   \         d7   p
Tf   TMTpT! T^ ,          P                  T
R7      T^,          T4      T
hRp
?
ii ; i)a6  Parse a 200 token-refresh response; return ``(payload, stripped access_token)``.

Each ``(message, code)`` pair keeps the provider's historical wording; ``{exc}`` in
*invalid_json*'s message is formatted with the JSON error. *strict_str* rejects non-string
access tokens; otherwise they are ``str()``-coerced.
c               ó(   € V ^8„  d   QhRRRRRRRR/# )r   Úmessager   Úcoder‚   rG   r   r   r   )r    s   "r!   r"   Ú3_refresh_payload_access_token.<locals>.__annotate__  s.   € ÷ Zñ Z�cð Z ð Z¨tð ZÈ9ñ Zr#   c                ó    <€ \        V SWR 7      # ))rr   rÍ   r¿   )r   )rÌ   rÍ   r‚   rr   s   &&&€r!   Ú_errÚ+_refresh_payload_access_token.<locals>._err  s   ø€ Ü˜¨8¸$ÔYÐYr#   N)ÚexcrE   r8   )	Újsonre   r    rK   rd   rJ   r   r9   r:   )rÃ   rr   rÅ   rÇ   rÈ   r¿   rÀ   rÁ   rÐ   ÚpayloadrÒ   r‚   Úaccesss   &d$$$$$$     r!   Ú_refresh_payload_access_tokenrÖ     sà   ø€ ð 7G÷ Zò ZðWØ—-‘-“/ˆô �gœt×$Ò$ØÒ'ÙÐ(Ñ)Ð)ØˆØ�[‰[˜Ó(€FßÜ#-¨f´c×#:Ò#:�—‘”À‰ä˜6Ó"ˆßÙ�NÑ#Ð#Øˆ?Ðøô ô WØ&:Ò&BÑ"ÐH\ˆÙ�< •?×)Ñ)¨cÐ)Ó2°LÀµOÀWÓMÐSVÐVûðWús   �B ÂC Â*1CÃC c               ó    € V ^8„  d   QhRRRR/# )r   rÒ   ÚBaseExceptionr   r   r   )r    s   "r!   r"   r"   '  s   € ÷ ñ ˜=ð ¨Sñ r#   c                óÆ   a€ ^ RI oW P                  V P                  3p\        ;QJ d    V3R lV 4       F  '       g   K   RM	  RM! V3R lV 4       4      '       g   R#  R# )u$  Actionable hint for device-login transport errors that look like TLS middlebox interference.

OpenSSL 3.5+ advertises post-quantum hybrid groups (e.g. X25519MLKEM768) by default, and some
intercepting middleboxes reject the resulting larger TLS 1.3 ClientHello â€” while curl, using a
different TLS stack, still works, so the failure masquerades as a Codex outage (#106384).
httpx wraps the ``ssl.SSLError`` in a ``ConnectError``/``ConnectTimeout`` whose text usually
repeats the OpenSSL message; the cause chain is checked too in case it doesn't.
Nc              3  óò   <a"  € T Fk  oSf   K	  \        SSP                  4      ;'       gC    \        ;QJ d#    V3R l\         4       F  '       g   K   RM	  RM! V3R l\         4       4      x € Km  	  R # 5i)Nc              3  ó>   <"  € T F  q\        S4      9   x € K  	  R # 5i©N)r   )Ú.0ÚmarkerÚerrs   & €r!   Ú	<genexpr>Ú._ssl_interop_hint.<locals>.<genexpr>.<genexpr>4  s   øé € Ð,cÑNbÀF´s¸3³xÖ-?ÓNbùs   ƒTF)rK   ÚSSLErrorÚanyÚ_SSL_TROUBLE_MARKERS)rÝ   rß   Ússls   &@€r!   rà   Ú$_ssl_interop_hint.<locals>.<genexpr>3  sP   ùé € ð áˆC˜Cô 	dŒ
�3˜Ÿ™Ó%×cÐc¯«Ô,cÕNbÓ,c¯¯ªÔ,cÕNbÓ,cÓ)cÔcÛùs   „A7�A7¬A7¸A7Á$A7TFr8   u�   This looks like a TLS handshake failure rather than a Codex outage: some networks reject the larger TLS 1.3 ClientHello that OpenSSL 3.5+ sends by default (post-quantum hybrid groups). Workaround: point OPENSSL_CONF at a config restricting Groups to classic curves (x25519:secp256r1:secp384r1:x448), or test with TLS 1.2 â€” see the Codex note in https://hermes-agent.nousresearch.com/docs/integrations/providers)rå   Ú	__cause__Ú__context__rã   )rÒ   Úchainrå   s   & @r!   Ú_ssl_interop_hintrê   '  sY   ø€ ó à—-‘- §¡Ð1€Eß‹3ô áó�3�3Š3ô áó÷ ò ñ ð	Mñr#   c               ó    € V ^8„  d   QhRRRR/# )r   rÒ   rØ   r   rG   r   )r    s   "r!   r"   r"   A  s   € ÷ ñ  }ð ¸ñ r#   c                óæ   € T p\        4       pVea   \        V4      V9  dQ   \        V\        P                  \
        34      '       d   R# VP                  \        V4      4       VP                  pKd  R# )a2  True when *exc* is transport-level (connection/TLS/socket) and safe to retry.

httpx raises ``httpx.TransportError`` subclasses wrapping the original ``ssl``/``socket``
error as ``__cause__``, so both spellings count. Anything else (decode errors, bugs) is
not a network blip and must surface immediately.
TF)ÚsetÚidrK   r   ÚTransportErrorÚOSErrorÚaddrç   )rÒ   rß   Úseens   &  r!   Ú_is_transient_transport_errorró   A  sW   € ð $'€CÜ“U€DØ
Š/œb ›g¨TÔ1Ü�cœE×0Ñ0´'Ð:×;Ò;ÙØ�‰”�C“ÔØ�m‰mŠÙr#   c               ó(   € V ^8„  d   QhRRRRRRRR/# )	r   Úurlr   ÚfailurerÆ   Úkwargsr   r   rÄ   r   )r    s   "r!   r"   r"   R  s(   € ÷ ñ ˜3ð ¨Oð Àsð È~ñ r#   c          	     ó´  € ^^rC  \        \        P                  ! R4      R7      ;_uu_ 4       pVP                  ! V 3/ VB uuRRR4       #   + '       g   i     KY  ; i  \         dm   pY48X  g   \        T4      '       g,   \        T^ ,           RT \        T4       2T^,          4      Th\        P                  ! T4       T^,          p Rp?KÎ  Rp?ii ; i)aT  One 15s POST for the device-login flow; transport errors become ``_codex_err(*failure)``.

A transient transport blip (a dropped connection mid-flow) is retried twice with a small
linear backoff before failing: losing the token exchange to a single SSL EOF wastes a
device-code approval the user already completed in the browser (#114610).
ç      .@©ÚtimeoutNz: )
Ú_codex_http_clientr   ÚTimeoutÚpostre   ró   r   rê   rM   Úsleep)rõ   rö   r÷   ÚattemptÚattemptsÚclientrÒ   s   &$,    r!   Ú_codex_login_postr  R  s´   € ð ˜1ˆXØ
ð	Ü#¬E¯MªM¸$Ó,?×@Õ@ÀFØ—{’{ 3Ñ1¨&Ñ1÷ A×@×@Ó@ûäô 	ØÔ"Ô*GÈ×*LÒ*LÜ Ø˜q•z�l " S EÔ*;¸CÓ*@Ð)AÐBÀGÈAÅJóPØUXðYä�JŠJ�wÔØ�q�LŽGûð	ús5   †(A  ®AÁ
A  ÁA	ÁA  ÁA  Á CÁ+A!CÃCc               ó   € V ^8„  d   QhRR/# )r   r   Útyper   )r    s   "r!   r"   r"   i  s   € ÷ %ñ % 4ñ %r#   c                 ó|   € \        \        RR4      p V e   V #  ! R R\        P                  4      pV\        n        V# )zýBuild the capped stream subclass on first use.

The base class is ``httpx.SyncByteStream``; naming it at module scope would resolve the
lazy ``httpx`` proxy at import time and put httpx back on the interactive-CLI startup path
(see ``auth_constants``).
ÚclsNc                  ó>   € ] tR tRtRtR R ltR R ltR R ltR	tR
# )Ú4_capped_byte_stream_class.<locals>._CappedByteStreamit  záBody stream that raises once more than ``_CODEX_AUTH_BODY_MAX_BYTES`` came off the wire.

httpx type-checks ``response.stream`` against ``SyncByteStream``, so the cap has to be a
stream subclass rather than a bare generator.
c               ó    € V ^8„  d   QhRRRR/# ©r   rÃ   rÄ   r   r>   r   )r    s   "r!   r"   ÚA_capped_byte_stream_class.<locals>._CappedByteStream.__annotate__{  s   € ÷ 	Bñ 	B ^ð 	B¸ñ 	Br#   c                	ó6   € WP                   uV n        V n        R # rÜ   )ÚstreamÚ	_responseÚ_raw)ÚselfrÃ   s   &&r!   Ú__init__Ú=_capped_byte_stream_class.<locals>._CappedByteStream.__init__{  s   € Ø(0·/±/Ð%ˆDŒN˜DžIr#   c               ó   € V ^8„  d   QhRR/# )r   r   zIterator[bytes]r   )r    s   "r!   r"   r  ~  s   € ÷ 
	ñ 
	˜oñ 
	r#   c              3  	ó  "  € ^ pV P                    Fr  pV\        V4      ,          pV\        8”  dO   V P                  4        \	        RV P
                  P                  P                   R\        R,           R2RRR7      hVx € Kt  	  R# 5i)	r%   zCodex auth response from z
 exceeded i   z KiB; refusing to parse it.Úcodex_auth_response_too_largeFr�   N)r  ÚlenÚ_CODEX_AUTH_BODY_MAX_BYTESÚcloser   r  rõ   Úhost)r  ÚtotalÚchunks   &  r!   Ú__iter__Ú=_capped_byte_stream_class.<locals>._CappedByteStream.__iter__~  s�   é € ØˆEØŸœ�Øœ˜U›Õ#�ØÔ5Ô5Ø—J‘J”LÜ$Ø3°D·N±N×4FÑ4F×4KÑ4KÐ3LÈJÜ5¸Õ=Ð>Ð>Yð[à7ÀôHð Hð ”ó #ùs   ‚BB	c               ó   € V ^8„  d   QhRR/# )r   r   r>   r   )r    s   "r!   r"   r  Š  s   € ÷ 	ñ 	˜4ñ 	r#   c                	ó:   € V P                   P                  4        R # rÜ   )r  r  )r  s   &r!   r  Ú:_capped_byte_stream_class.<locals>._CappedByteStream.closeŠ  s   € Ø�I‰I�O‰OÖr#   )r  r  N)	Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__doc__r  r  r  Ú__static_attributes__r   r#   r!   Ú_CappedByteStreamr	  t  s   † ñ	õ	Bõ
	÷	ñ 	r#   r(  )ÚgetattrÚ_capped_byte_stream_classr   ÚSyncByteStreamr  )Úcachedr(  s     r!   r*  r*  i  sA   € ô Ô.°°tÓ<€FØÒØˆôœE×0Ñ0ô ð2 %6ÔÔ!ØÐr#   c               ó    € V ^8„  d   QhRRRR/# r  r   )r    s   "r!   r"   r"   ‘  s   € ÷ <ñ < ~ð <¸$ñ <r#   c                ó0   € \        4       ! V 4      V n        R# )ac  httpx response hook: refuse to buffer an auth body above ``_CODEX_AUTH_BODY_MAX_BYTES``.

Runs before ``client.post()`` reads the body, so a hostile or broken endpoint/proxy answering
200 with megabytes of "JSON" is cut off at the cap instead of being fully buffered and parsed
(#55253). Same cap for every status: error bodies are small diagnostics too.
N)r*  r  )rÃ   s   &r!   Ú_cap_codex_response_bodyr/  ‘  s   € ô 0Ô1°(Ó;€H†Or#   c               ó    € V ^8„  d   QhRRRR/# )r   r÷   r   r   zhttpx.Clientr   )r    s   "r!   r"   r"   ›  s   € ÷ ñ  ð ¨ñ r#   c                 óÜ   € \        4        \        P                  ! RRR\        .//V B p\	        \
        4      ;_uu_ 4        ^ RIHp V! V4       RRR4       V#   + '       g   i     T# ; i)aõ  Build an ``httpx.Client`` for Codex OAuth/probe endpoints with Happy-Eyeballs racing and a
1 MiB response-body cap (``_cap_codex_response_body``).

A host advertising AAAA records but blackholing IPv6 makes each serial connect eat the full
timeout before IPv4 is tried (same failure mode as the chat transport). Best-effort: if the
racing backend can't be installed (mocked client in tests), serial connect behavior remains.

Same broken-IPv6 failure mode as the chat transport (#13834): a host that advertises AAAA records but
blackholes IPv6 makes each serial connect attempt eat the full connect timeout before IPv4 is tried, so
token refresh / device login / usage probes time out where the official Codex CLI (which races families
per RFC 8305) works.
Úevent_hooksrÃ   )Úenable_happy_eyeballs_on_clientNr   )r   r   ÚClientr/  r   re   Úagent.process_bootstrapr3  )r÷   r  r3  s   ,  r!   rü   rü   ›  s\   € ô Ôä�\Š\ÑY zÔ4LÐ3MÐ&NÐYÐRXÑY€FÜ	”)×	Õ	ÝKÙ'¨Ô/÷ 
ð €M÷ 
Ö	ð €Mús   ÁAÁA+	c               ó    € V ^8„  d   QhRRRR/# )r   Úretry_afterr-   r   r   r   )r    s   "r!   r"   r"   ±  s   € ÷ Gñ G¨mð GÀ	ñ Gr#   c                ó>   € V e   RV  R2MRp\        V\        RR7      # )Nz2Codex provider quota exhausted (429); retry after zs. Credentials are still valid.zfCodex provider quota exhausted (429). Credentials are still valid; retry after the usage limit resets.Fr�   )r   r   )r7  rÌ   s   & r!   Ú_codex_quota_exhausted_errorr9  ±  s:   € ð Ò"ð =¸[¸Mð J'ñ 	'ð	.ð	 ô �gÔ6ÀÔFÐFr#   c               ó    € V ^8„  d   QhRRRR/# )r   rÃ   rÄ   r   r   r   )r    s   "r!   r"   r"   »  s   € ÷ $?ñ $?¨>ð $?¸iñ $?r#   c                ó6  € ^ RI Hp RpRV P                   R2p V P                  4       p\	        V\
        4      '       Ed   VP                  R4      p\	        V\
        4      '       d|   VP                  R4      ;'       g    VP                  R4      pV! V4      '       d   VP                  4       pVP                  R4      pV! V4      '       d   R	VP                  4        2pMiV! V4      '       d\   VP                  4       pVP                  R
4      ;'       g    VP                  R4      pV! V4      '       d   R	VP                  4        2pVR8X  d   R\        4        R2pVR9   ;'       g    V P                  R9   p	\        W2V	R7      #   \         d     LKi ; i)zFDecode a non-200 Codex token-refresh response into a shaped AuthError.)r€   Úcodex_refresh_failedz'Codex token refresh failed with status Ú.ÚerrorrÍ   r  rÌ   zCodex token refresh failed: Úerror_descriptionÚrefresh_token_reusedz£Codex refresh token was already consumed by another client (e.g. Codex CLI or VS Code extension). Run `codex` in your terminal to generate fresh tokens, then run `r…   r�   >   Úinvalid_grantÚinvalid_tokenÚinvalid_requestr@  >   é‘  é“  )rA   r€   Ústatus_coderÓ   rK   rd   rJ   r9   re   r*   r   )
rÃ   r€   rÍ   rÌ   rß   Úerr_objÚnested_codeÚ
nested_msgÚerr_descr¿   s
   &         r!   Ú_codex_refresh_failure_errorrK  »  s‰  € å-Ø!€DØ7¸×8LÑ8LÐ7MÈQÐO€GðØ�m‰m‹oˆÜ�cœ4× Ó Ø—g‘g˜gÓ&ˆGä˜'¤4×(Ò(Ø%Ÿk™k¨&Ó1×HÐH°W·[±[ÀÓ5H�Ù  ×-Ò-Ø&×,Ñ,Ó.�DØ$Ÿ[™[¨Ó3�
Ù  ×,Ò,Ø <¸Z×=MÑ=MÓ=OÐ<PÐQ�Gøá˜w×'Ò'Ø—}‘}“�ØŸ7™7Ð#6Ó7×MÐM¸3¿7¹7À9Ó;M�Ù  ×*Ò*Ø <¸X¿^¹^Ó=MÐ<NÐO�Gð Ð%Ô%ðô 0Ó1Ð2Ð2GðIð 	ð 	Ð]Ñ]÷ 	.ð 	.Ø×Ñ :Ñ-ð ô �gÐ-=Ô>Ð>øô ô Ùðús<   šA%F
 Â F
 Â .F
 ÃF
 Ã#F
 Ã1'F
 ÄF
 Ä9F
 Æ
FÆFÚtimeout_secondsg      4@c               ó(   € V ^8„  d   QhRRRRRRRR/# )r   rE   r   rˆ   rL  rL   r   r=   r   )r    s   "r!   r"   r"   â  s,   € ÷ %ñ %Øð%Ø&)ð%Ø?Dð%ØP^ñ%r#   c               óè  € ^ RI HpHp ? V! V4      '       g+   \        \        P                  \        4       R7      RRR7      h\        \        P                  ! \        R\        V4      4      4      RRR\        /R	7      ;_uu_ 4       pVP                  \        R
R/RRRVR\        /R7      pRRR4       XP                   R8X  d    \#        \%        \'        VRR4      4      4      hVP                   ^È8w  d   \)        V4      h\+        VRRRRRR7      w  rxRVRVP-                  4       RV! 4       /p	VP/                  R4      p
V! V
4      '       d   V
P-                  4       V	R&   V	#   + '       g   i     L¾; i)z>Refresh Codex OAuth tokens without mutating Hermes auth state.)r€   r¦   r�   r‰   Tç      @ÚAcceptúapplication/jsonú
User-Agent)rû   r,   úContent-Typeú!application/x-www-form-urlencodedÚ
grant_typerˆ   Ú	client_id)r,   ÚdataNé­  r,   r(   F)rr   rÇ   rÅ   rÀ   rÈ   rE   rp   )z*Codex token refresh returned invalid JSON.Úcodex_refresh_invalid_json)z6Codex token refresh response was missing access_token.Ú"codex_refresh_missing_access_token)rA   r€   r¦   r   rŒ   r    r*   rü   r   rý   rN   rL   r   rþ   r   r   rF  r9  r3   r)  rK  rÖ   r9   rJ   )rE   rˆ   rL  r€   r¦   r  rÃ   Úrefresh_payloadÚrefreshed_accessÚupdatedÚnext_refreshs   &&$        r!   Úrefresh_codex_oauth_purer_  â  s}  € ÷ :ØÙ˜×'Ò'ÜÔ3×:Ñ:ÔCYÓC[Ð:Ó\Ø;ÀTôKð 	Kä	Ü—’œc #¤u¨_Ó'=Ó>Ó?ØÐ-¨|Ô=SÐT÷
Võ 
VàY_Ø—;‘;Ü!¨NÐ<_Ð+`à˜o¨ÀØÔ2ð4ð ó 5ˆ÷
Vð ×Ñ˜sÔ"ô +Ü&¤w¨x¸ÀDÓ'IÓJóLð 	Là×Ñ˜sÔ"Ü*¨8Ó4Ð4Ü(EØ˜>¸DØaà"ð2ô)3Ñ%€Oð 	Ð(¨/¸=×;NÑ;NÓ;PØ™
›ð&€Gð #×&Ñ& Ó7€LÙ�\×"Ò"Ø#/×#5Ñ#5Ó#7ˆ�Ñ Ø€N÷;
V÷ 
Vús   Â%E!Å!E1	c               ó$   € V ^8„  d   QhRRRRRR/# )r   r„   r‘   rL  rL   r   r   )r    s   "r!   r"   r"   
  s"   € ÷ *ñ * ~ð *Èð *ÐR`ñ *r#   c                ó  € ^ RI HpHpHp \	        \        \        4      \        V4      R,           4      pV! RV4      ;_uu_ 4       w  rgpT;'       g    / P                  R4      p	\        V	\        4      '       d   T	M/ p	\        V	P                  R4      4      \        V	P                  R4      4      rºV
'       dQ   V'       dI   V\        V P                  R4      4      8w  d*   \        P                  R4       / V CRV
RV/CuuRRR4       #  T! \        V P                  RR	4      ;'       g    R	4      \        V P                  RR	4      ;'       g    R	4      VR
7      p/ T CRTR,          RTR,          /CpT! TRR7       RRR4       T#   \         dc   p\        TRR4      '       g   h \!        R\        TRR4      ;'       g    R 2T
;'       g    RR7      pT'       g   h Tu Rp?uuRRR4       # Rp?ii ; i  + '       g   i     X# ; i)a  Refresh Codex access token using the refresh token.

The whole re-read -> endpoint POST -> write-back runs inside the SOURCE store's transaction:
two profiles borrowing the same root grant otherwise both submit the same single-use refresh
token (each holds only its own profile lock) and OpenAI revokes the family. A waiter that
finds root already rotated by its peer adopts the stored pair instead of replaying the
consumed token. Both locks wait out a full endpoint timeout so the waiter adopts, not times out.
)r¢   r¬   r_  rO  r(   r„   rE   rˆ   uK   Codex refresh token already rotated by a peer â€” adopting the stored pair.Nr8   ©rL  r¿   Fzrefresh_token rejected: rÍ   Ú
auth_error©r¯   T)r�   )rA   r¢   r¬   r_  rN   rL   r   rJ   rK   rd   r:   rf   r¶   r   r   r)  r¾   )r„   rL  r¢   r¬   r_  Úlock_timeoutrº   rŽ   r»   r¼   Ú	stored_atÚ	stored_rtÚ	refreshedrÒ   r¸   Úupdated_tokenss   &&              r!   Ú_refresh_codex_auth_tokensrj  
  sÛ  € ÷ jÑiÜ”uÔ6Ó7¼¸Ó9OÐRUÕ9UÓV€LÙ	$ ^°\×	BÔ	BÑF^ÀvÐV]Ø—+�+˜2×"Ñ" 8Ó,ˆÜ% f¬d×3Ò3‘¸ˆÜ(¨¯©°NÓ)CÓDÄiÐPV×PZÑPZÐ[jÓPkÓFl�9ßŸ y´I¸f¿j¹jÈÓ>YÓ4ZÔ'ZÜ�K‰KÐeÔfØT�fÐT˜n¨i¸È)ÑT÷ 
CÒ	Bð	Ù0Ü�F—J‘J˜~¨rÓ2×8Ð8°bÓ9¼3¸v¿z¹zÈ/Ð[]Ó?^×?dÐ?dÐbdÓ;eØ /ô1ˆIð$9Øð9Ø$ i°Õ&?Ø˜Y Õ7ñ9ˆñ 	˜>¸Õ>÷= 
Cð> Ðøô) ô 	ô ˜3Ð 2°E×:Ò:ØÜ5Ø*¬7°3¸ÀÓ+E×+UÐ+UÈÐ*VÐWØ&/×&7Ð&7°4ô9ˆH÷ ØØŒO÷3 
CÒ	Bûð	ú÷ 
CÖ	Bð> Ðúsa   ÁA;G7Ã=G7Ä
FÄ*#FÅFÅ"G7ÆG4Æ-G/Ç G/ÇG/ÇG/ÇG4Ç G7Ç/G4Ç4G7Ç7H	c               ó   € V ^8„  d   QhRR/# )r   r   r“   r   )r    s   "r!   r"   r"   7  s   € ÷ ñ Ð":ñ r#   c                 ó°  € ^ RI Hp  \        P                  ! RR4      P	                  4       ;'       g&    \        \        P                  ! 4       R,          4      p\        V4      P                  4       R,          pVP                  4       '       g   R#  \        P                  ! VP                  RR7      4      P                  R	4      p\        V\        4      '       d/   VP                  R
4      '       d   VP                  R4      '       g   R# V ! VR
,          ^ 4      '       d   \         P#                  RV4       R# \        V4      #   \$         d     R# i ; i)zWRead ~/.codex/auth.json (Codex CLI file) tokens if valid and not expired; never writes.)rO   Ú
CODEX_HOMEr8   z.codexz	auth.jsonNz	utf-8-sig)Úencodingr„   rE   rˆ   u7   Codex CLI tokens at %s are expired â€” skipping import.)rA   rO   rT   rU   r9   r   r   ÚhomeÚ
expanduserÚis_filerÓ   ÚloadsÚ	read_textrJ   rK   rd   rf   rg   re   )rO   Ú
codex_homeÚ	auth_pathr„   s       r!   r³   r³   7  sü   € å?Ü—’˜<¨Ó,×2Ñ2Ó4×SÐS¼¼D¿IºI»KÈ(Õ<RÓ8S€JÜ�ZÓ ×+Ñ+Ó-°Õ;€IØ×Ñ×ÒÙðÜ—’˜I×/Ñ/¸Ð/ÓEÓF×JÑJÈ8ÓTˆÜ˜6¤4×(Ò(¨V¯Z©Z¸×-GÒ-GØ—J‘J˜×/Ò/Ùñ +¨6°.Õ+AÀ1×EÒEÜ�L‰LÐRÐT]Ô^ÙÜ�F‹|ÐøÜô Úðús+   ÂA!E Ã4E ÄE Ä#E Ä;
E ÅEÅEÚforce_refreshÚrefresh_if_expiringÚrefresh_skew_secondsÚ	read_onlyc          
     ó,   € V ^8„  d   QhRRRRRRRRRR/# )	r   rv  rG   rw  rx  r1   ry  r   r=   r   )r    s   "r!   r"   r"   M  sF   € ÷ fYñ fYØðfYØ9=ðfYàðfYð ðfYð !/ñfYr#   c           	     ó  a aaaa€ ^ RI HpHoHpHp RpRpRp	 S'       d   V! RR7      pMyT! 4       ;_uu_ 4        ^ RI Hp
Hp V! V
! 4       R4      ;'       g    / P                  R4      p\        V\        4      '       d   VP                  R4      MRp	V! RR7      pRRR4       VEf    \        4       w  ppV'       dA   S '       d9   S'       g1   ^ RIHp V! R4      P%                  VR7      pVe   VP&                  MRpV'       d   \)        VRR\+        V4      R7      # \-        4       pV'       dé   S'       g^   \/        V4      '       dM   \0        P3                  R4       \5        4        \        4       w  ppV'       d   \)        VRR\+        V4      R7      # VP                  R4      p\        V\6        \8        34      ;'       d    V\:        P:                  ! 4       8„  p\=        V'       d*   \7        V\:        P:                  ! 4       ,
          4      4      hR4      hVe   Vh\?        \@        PC                  \E        4       R7      RRR7      h\        VR,          4      p\G        VP                  R4      4      p\I        R^4      pR VV VVV3R llpV! V4      '       d¢   \K        \9        \L        4      VR,           4      pV! VR7      ;_uu_ 4        V! RR7      p\        VR,          4      pV! \G        VP                  R4      4      4      '       d   \O        VV4      p\G        VP                  R4      4      pRRR4       \)        VRVP                  R4      R7      #   + '       g   i     ELÆ; i  \         d€   pTpS'       gk   TP                  '       dY   TP                  R9   dH   \        \        TP                  ;'       g    R	4      T	R
7      pT'       d   RTRTP                  R4      /p Rp?ELNRp?ii ; i  + '       g   i     LÎ; i)u  Resolve runtime credentials from Hermes's own Codex token store.

``read_only=True`` (status / doctor / pickers) reports the stored state as-is: no Codex CLI
adoption, no token refresh, no auth-store write â€” and it wins over ``force_refresh``. A
diagnostic that silently imports another program's rotating refresh token or spends one is a
mutation the user never asked for (#68004).

Falls back to the credential pool when the singleton (``providers.openai-codex.tokens``) has no
usable access_token but the pool (``credential_pool.openai-codex``) does.

This closes the divergence between the chat path (singleton-only via this function) and the auxiliary
path (pool-first via ``auxiliary_client._resolve_codex_credential_and_base``). Without this fallback, a user whose tokens live only
in the pool â€” for example after a manual pool seed, a partial re-auth, or pool-only restoration from a
backup â€” gets a bare HTTP 401 ``Missing Authentication header`` from the wire instead of a usable
credential. See issue #32992.
)ry   rO   Ú_probe_codex_quota_restoredr�   NF)r|   )rz   r   r(   r„   rE   rc  rd  rp   )Ú	load_pool)Úapi_key_hintr8   Úcredential_pool)ro   rp   rl   uB   Codex quota restored upstream â€” clearing stale pool cooldown(s).Úreset_atr�   rƒ   TÚ$HERMES_CODEX_REFRESH_TIMEOUT_SECONDSc               ó    € V ^8„  d   QhRRRR/# )r   Útokenr   r   rG   r   )r    s   "r!   r"   Ú7resolve_codex_runtime_credentials.<locals>.__annotate__£  s   € ÷ bñ b˜sð b tñ br#   c                ód   <€ S'       d   R # \        S4      ;'       g    S;'       d
    S! V S4      # )F)rG   )rƒ  rO   rv  ry  rw  rx  s   &€€€€€r!   Ú_should_refreshÚ:resolve_codex_runtime_credentials.<locals>._should_refresh£  s7   ø€ ßÙÜ�MÓ"÷ bð bØ×`Ð`Ñ$CÀEÐK_Ó$`ð	br#   rO  rb  zhermes-auth-store)ro   rp   >   r†   r‡   r‰   )(rA   ry   rO   r|  r�   rz   r   rJ   rK   rd   r   r¿   rÍ   r¾   r   Ú_pool_codex_credentialr´   r}  Útry_refresh_matchingÚruntime_api_keyru   rk   Ú_codex_pool_rate_limit_statusÚ&_probe_codex_pool_entry_quota_restoredrf   r¶   Ú clear_codex_pool_quota_cooldownsr1   rL   rM   r9  r   rŠ   r    r*   r:   r   rN   r   rj  )rv  rw  rx  ry  ry   r|  r�   Ú
read_errorrW  r¹   rz   r   ÚrawrÒ   r¸   Ú
pool_tokenÚ	pool_baser}  rh  Úpool_rate_limitr€  Ú	in_futurer„   rE   Úrefresh_timeout_secondsr†  re  rO   s   dddd                       @r!   Ú!resolve_codex_runtime_credentialsr•  M  s]  ü€ ÷(ó ð '+€JØ€DØ"€HðZßñ &¨EÔ2‰Dá!×#Õ#÷ SÙ+Ñ,<Ó,>ÀÓO×UÐUÐSU×ZÑZÐ[cÓd�Ü6@ÀÄd×6KÒ6K˜3Ÿ7™7 >Ô2ÐQU�Ù)°Ô6�÷ $ð  ƒ|Ü 6Ó 8Ñˆ
�IßŸ-·	å7Ù! .Ó1×FÑFÐT^ÐFÓ_ˆIØ6?Ò6K˜×2Ò2ÐQSˆJßô )¨Ð<MÐ\`Ü2LÈYÓ2WôYð Yä7Ó9ˆß÷
 Ô!GÈ×!XÒ!XÜ—‘Ð`ÔaÜ0Ô2Ü(>Ó(@Ñ%�
˜IßÜ0Ø"Ð+<È4Ü!;¸IÓ!FôHð Hð '×*Ñ*¨:Ó6ˆHÜ" 8¬c´5¨\Ó:×UÐU¸xÌ$Ï)Ê)Ë+Ñ?UˆIÜ.Ïi¬s°8¼d¿iºi»kÕ3IÓ/JÓbÐbÐ]aÓbÐbØÒ!ØÐÜÔ,×3Ñ3Ô<RÓ<TÐ3ÓUØ-°tô=ð 	=ä�$�x•.Ó!€FÜ˜VŸZ™Z¨Ó7Ó8€LÜ'Ð(NÐPRÓSÐ÷bô bñ �|×$Ò$äœ5Ô!:Ó;Ð=TÐWZÕ=ZÓ[ˆÙ¨l×;Ö;Ù%¨EÔ2ˆDÜ˜$˜x�.Ó)ˆFÙœy¨¯©°NÓ)CÓD×EÒEÜ3°FÐ<SÓT�Ü$ V§Z¡Z°Ó%?Ó@ˆL÷ <ô !ØÐ0¸t¿x¹xÈÓ?WôYð Y÷I $×#Ð#ûô ô ZØˆ
ß˜S×1×1Ð1°c·h±hð C(ô 7(ô 6Ü�C—H‘H×,Ð, Ó-ÀXôOˆHçØ  (¨N¸H¿L¹LÈÓ<XÐY�ÿùðZú÷n <×;ús^   ™M- ¡	M- «M- ºA"MÂM- ËA(O:ÍM*	Í$M- Í*M- Í-O7Í8O2Î-O2ÏO2ÏO2Ï2O7Ï:P
	c               ó(   € V ^8„  d   QhRRRRRRRR/# )r   rÍ   r   r®   rÌ   r   rG   r   )r    s   "r!   r"   r"   ¶  s.   € ÷ Pñ P cð P°3ð PÀð PÈñ Pr#   c                ó¦  aa€ \        T;'       g    R4      P                  4       \        T;'       g    R4      P                  4       uooV R8H  ;'       g}    \        ;QJ d    V3R lR 4       F  '       g   K   RM	  RM! V3R lR 4       4      ;'       g;    \        ;QJ d    V3R lR 4       F  '       g   K   R# 	  R# ! V3R lR 4       4      # )	zITrue when persisted pool-entry error metadata describes a 429/quota stop.r8   rX  c              3  ó,   <"  € T F	  qS9   x € K  	  R # 5irÜ   r   )rÝ   ÚkÚreason_ls   & €r!   rà   Ú._is_codex_rate_limit_shaped.<locals>.<genexpr>»  s   øé € ÐMÑ&L �HŽ}Ó&Lùó   ƒTFc              3  ó,   <"  € T F	  qS9   x € K  	  R # 5irÜ   r   )rÝ   r™  Ú	message_ls   & €r!   rà   r›  ¼  s   øé € ÐNÑ'M !�IŽ~Ó'Mùrœ  )Ú
rate_limitÚusage_limitÚquota)z
rate limitzusage limitr¡  )r   Úlowerrã   )rÍ   r®   rÌ   rž  rš  s   &&&@@r!   Ú_is_codex_rate_limit_shapedr£  ¶  s°   ù€ ä˜fŸl˜l¨Ó+×1Ñ1Ó3´S¸¿¸ÀBÓ5G×5MÑ5MÓ5OÐ€Hˆià�‰÷ 	Oð 	Oß‹3ÔMÑ&LÓM�3�3Š3ÔMÑ&LÓMÓM÷	Oð 	Oç‹3ÔNÑ'MÓN�3Œ3ðPò ðPð ÔNÑ'MÓNÓNðPr#   c               ó    € V ^8„  d   QhRRRR/# )r   r<   r=   r   rG   r   )r    s   "r!   r"   r"   ¿  s   € ÷ )ñ )¨.ð )¸Tñ )r#   c                ó®   € V P                  R4      R8H  ;'       d;    \        V P                  R4      V P                  R4      V P                  R4      4      # )zFPool entry frozen by a 429/quota stop (as opposed to an auth failure).Úlast_statusÚ	exhaustedÚlast_error_codeÚlast_error_reasonÚlast_error_message)rJ   r£  )r<   s   &r!   Ú_entry_is_rate_limit_exhaustedr«  ¿  sP   € à�9‰9�]Ó# {Ñ2÷ )ð )Ô7RØ�	‰	Ð#Ó$ e§i¡iÐ0CÓ&DØ�	‰	Ð&Ó'ó8)ð )r#   i,  z'dict[str, tuple[float, Optional[bool]]]Ú_codex_quota_probe_cachec               ó    € V ^8„  d   QhRRRR/# )r   rƒ  r   r   r   )r    s   "r!   r"   r"   Í  s   € ÷ Bñ B¨ð B°ñ Br#   c                óv   € \         P                  ! V P                  R 4      4      P                  4       R,          # )zutf-8:Né   N)ÚhashlibÚsha256ÚencodeÚ	hexdigest)rƒ  s   &r!   Ú_codex_quota_probe_cache_keyr´  Í  s(   € Ü�>Š>˜%Ÿ,™, wÓ/Ó0×:Ñ:Ó<¸SÕAÐAr#   c               ó    € V ^8„  d   QhRRRR/# )r   rl   rZ   r   r   r   )r    s   "r!   r"   r"   Ñ  s   € ÷ 
ñ 
 ]ð 
°sñ 
r#   c                ó²   € \        V 4      P                  R4      ;'       g    \        4       pVP                  R4      pTRV9   d   RMR,           pVR,           # )a'  Resolve the Codex usage endpoint for a probe.

Mirrors the Codex CLI's PathStyle split: base URLs containing ``/backend-api`` use the ChatGPT
``/wham/usage`` path, everything else ``/api/codex/usage``. Kept local so this low-level auth
module does not import the auxiliary account-usage module.
rS   z/codexz/backend-apiz/whamz
/api/codexz/usage)r:   rV   rW   Úremovesuffix)rl   Ú
normalizedÚprefixs   &  r!   Ú_codex_usage_probe_urlrº  Ñ  sR   € ô ˜8Ó$×+Ñ+¨CÓ0×EÐE´OÓ4E€JØ×(Ñ(¨Ó2€JØ n¸
Ô&B™7ÈÕU€FØ�HÕÐr#   Úmin_interval_secondsc               ó(   € V ^8„  d   QhRRRRRRRR/# )	r   rE   r   rl   rZ   r»  rL   r   rÉ   r   )r    s   "r!   r"   r"   Þ  s,   € ÷ 6ñ 6Øð6Ø$1ð6àð6àM[ñ6r#   c               óŽ  € ^ RI HpHp \        V 4      pV'       d   \	        V4      '       g   R# \        V4      p\        P                  ! 4       p\        ;_uu_ 4        VP                  V4      pVe(   Wx^ ,          ,
          V8  d   V^,          uuRRR4       # VR3W6&   RRR4       Rp	 ^ RI
Hp
 RRV 2RRRR	/V
! V4      Cp\        R
R7      ;_uu_ 4       pVP                  \        V4      VR7      pRRR4       XP                  ^È8X  Ed   VP                  4       ;'       g    / pVP                  R4      ;'       g    / .VP                  R4      ;'       g    .  Uu. uF6  p\!        V\"        4      '       g   K  VP                  R4      ;'       g    / NK8  	  up,           pRpV Fs  pR Fj  pVP                  V4      ;'       g    / P                  R4      p\!        V\$        \&        34      '       g   KK  \)        T;'       g    R\'        V4      4      pKl  	  Ku  	  Ve   VR8  p	MVP                  R8X  d   Rp	\        ;_uu_ 4        Wy3W6&   RRR4       V	#   + '       g   i     ELÅ; i  + '       g   i     EL„; iu upi   \*         d    \,        P/                  RRR7       Rp	 Lqi ; i  + '       g   i     T	# ; i)z·Ask the Codex usage endpoint whether this account's quota is usable again.

Probes are throttled per access token (module-local cache) so the hot selection path can fire
this freely.
)r¬  r€   N)Úcodex_account_headersÚAuthorizationzBearer rP  rQ  rR  z	codex-clig      $@rú   )r,   rŸ  Úadditional_rate_limitsÚused_percentg        g      Y@rX  FzCodex quota probe failedTr_   )Úprimary_windowÚsecondary_window)rA   r¬  r€   r:   r   r´  rM   Ú	monotonicÚ_codex_quota_probe_lockrJ   Úagent.codex_headersr¾  rü   rº  rF  rÓ   rK   rd   r1   rL   rN   re   rf   rg   )rE   rl   r»  r¬  r€   rƒ  Ú	cache_keyÚnowr,  Úresultr¾  r,   r  rÃ   rÔ   ÚextraÚwindowsÚ
worst_usedrŸ  ÚkeyÚuseds   &$$                  r!   r|  r|  Þ  s{  € ÷ HÜ�lÓ#€E÷ Ô*¨5×1Ò1ÙÜ,¨UÓ3€IÜ
�.Š.Ó
€Cß	 Ö	 Ø)×-Ñ-¨iÓ8ˆØÒ 3°­¥?Ð6JÔ"JØ˜!•9÷ 
!Ò	 ð
 03°D¨kÐ Ñ+÷ 
!ð "€Fðå=à˜w u gÐ.°Ð:LØ˜+ðGá)>¸uÓ)EðGˆô  ¨×-Õ-°Ø—z‘zÔ"8¸Ó"BÈG�zÓTˆH÷ .à×Ñ 3Õ&Ø—m‘m“o×+Ð+¨ˆGð —{‘{ <Ó0×6Ð6°BÐ7à%Ÿk™kÐ*BÓC×IÐIÀrÐIó;,áI�EÜ˜e¤T×*ô .�—	‘	˜,Ó'×-Ð-¨2Ò-ÙIñ;,õ ,ˆGð +/ˆJÛ%�
ÛA�CØ&ŸN™N¨3Ó/×5Ð5°2×:Ñ:¸>ÓJ�DÜ! $¬¬e¨×5Ô5Ü%(¨×):Ð):°s¼EÀ$»KÓ%Hš
ó Bñ &ð
 Ò%Ø# eÑ+�øØ×!Ñ! SÔ(ØˆF÷ 
!Ö	 Ø/2¨mÐ Ñ+÷ 
!à€M÷M 
!×	 Ð	 ú÷ .×-Ð-üò;,øô ô Ü�‰Ð/¸$ˆÔ?ØŠðú÷ 
!Ö	 à€MúsŠ   Á2IÂIÂ+-J	 ÃI0Ã50J	 Ä&J	 ÅJ	 ÅJ	 Å!JÅ>JÆJÆ,J	 Ç
+J	 Ç:J	 È"J	 È+J	 ÉJ3ÉI-	É0J	É;J	 Ê	$J0Ê/J0Ê3K	c               ó(   € V ^8„  d   QhRRRRRRRR/# )r   rE   r   rˆ   r»  rL   r   úOptional[dict[str, Any]]r   )r    s   "r!   r"   r"     s,   € ÷ "ñ "Øð"Ø&)ð"àð"àMeñ"r#   c               óH  € ^ RI Hp \        V 4      \        V4      rTV'       d   V'       d   \        V^ 4      '       g   R# \	        V4      p\
        P                  ! 4       p\        ;_uu_ 4        VP                  V4      pVe    Wx^ ,          ,
          V8  d    RRR4       R# RRR4        \        WE4      #   + '       g   i     L; i  \         dM    \        P                  RRR7       \        ;_uu_ 4        TR3Y6&   RRR4       M  + '       g   i     M; i R# i ; i)uß  Refresh an EXPIRED stored access token so the quota probe can get a real answer.

Exhausted pool entries are skipped by the proactive refresh chain (#44799), so by the time
anything probes with the stored token it has expired; the usage endpoint answers
``401 token_expired``, the probe returns None, and the cooldown is kept until
``last_error_reset_at`` no matter what happened upstream (top-up, plan upgrade) â€” #89415.
Returns the rotated token pair (callers MUST persist it: refresh tokens are single-use) or
None when no refresh was needed/possible. The cooldown itself is left untouched.

Shares the probe's per-token throttle: a refresh that keeps failing (revoked grant, network
down) would otherwise POST to the token endpoint on every credential selection, while the
probe itself is capped at one call per ``min_interval_seconds``. A failed attempt reserves
the stale token's probe slot, so neither the refresh nor the doomed 401 probe fire again
until the interval has elapsed.
)r¬  Nz$Codex pre-probe token refresh failedTr_   )rA   r¬  r:   rO   r´  rM   rÄ  rÅ  rJ   r_  re   rf   rg   )	rE   rˆ   r»  r¬  rƒ  ÚrefreshrÇ  rÈ  r,  s	   &&$      r!   Ú"_refresh_expired_codex_probe_tokenrÓ    sÚ   € õ$ 9Ü˜|Ó,¬i¸Ó.Fˆ7ßŸÔ'FÀuÈa×'PÒ'PÙÜ,¨UÓ3€IÜ
�.Š.Ó
€Cß	 Ö	 Ø)×-Ñ-¨iÓ8ˆØÒ 3°­¥?Ð6JÔ"JØ÷ 
!×	 Ñ	 ðÜ'¨Ó7Ð7÷ 
!×	 ûô ô Ü�‰Ð;ÀdˆÔKß$Ö$Ø36¸°+Ð$Ñ/÷ %×$×$Ð$úâð	ús6   Á.+B7Â,
C
 Â7C	Ã
0D!Ã:D
Ä	D!Ä
DÄD!Ä D!c               ó    € V ^8„  d   QhRRRR/# )r   r<   r=   r   rÉ   r   )r    s   "r!   r"   r"   <  s   € ÷ jñ j°.ð jÀ^ñ jr#   c                ól  € ^ RI HpHpHp \	        V P                  R4      4      p\        W@P                  R4      4      pV'       d†   VR,          p V! 4       ;_uu_ 4        V! 4       p\        \        VR4      4       FC  pVP                  R4      V P                  R4      8X  g   K*  VP                  V4       V! V4        M	  RRR4       V'       g   R# \        V\        V P                  R
4      4      R7      #   + '       g   i     L?; i  \         d    \        P                  RRR	7        Lfi ; i)z^``_probe_codex_quota_restored`` for a persisted pool entry, refreshing an expired token first.©ry   rz   r¤   rE   rˆ   r(   rî   Nz-Failed to persist refreshed Codex pool tokensTr_   rl   )rl   )rA   ry   rz   r¤   r:   rJ   rÓ  r—   r–   r©   re   rf   rg   r|  rk   )r<   ry   rz   r¤   rƒ  Úfreshr�   Ú
disk_entrys   &       r!   rŒ  rŒ  <  sõ   € çTÑTÜ�e—i‘i Ó/Ó0€EÜ.¨u·i±iÀÓ6PÓQ€EßØ�nÕ%ˆð		YÙ!×#Õ#Ù-Ó/�
Ü"3´MÀ*ÈnÓ4]Ö"^�JØ!—~‘~ dÓ+¨u¯y©y¸«Ö>Ø"×)Ñ)¨%Ô0Ù(¨Ô4Ùñ	 #_÷ $÷ Ùä& uÔ7QÐRW×R[ÑR[Ð\fÓRgÓ7hÔiÐi÷ $×#ûô ô 	YÜ�L‰LÐHÐSWˆLÖXð	Yús7   ÁD Á AC;Â'C;ÃD Ã;D	ÄD ÄD Ä"D3Ä2D3c               ó    € V ^8„  d   QhRRRR/# )r   rE   rZ   r   r1   r   )r    s   "r!   r"   r"   S  s   € ÷ ñ °=ð ÈCñ r#   c                ó*  € ^ RI HpHp ^ RIHpHpHp ^ p V! R4      '       d   RMV! 4       pV! VR7      ;_uu_ 4        V! V4      p\        \        VR4      4       F]  p	V '       d,   \        V	P                  R4      ;'       g    R4      V 8w  d   K6  \        V	4      '       g   KI  \        V	4       V^,          pK_  	  V'       d
   V! W‡R7       RRR4       V#   + '       g   i     T# ; i  \         d    \        P                  RR	R
7        T# i ; i)aä  Clear rate-limit cooldowns on persisted openai-codex pool entries.

Called after the upstream quota is KNOWN to be restored (a ``/usage reset`` redemption or a
positive live probe) so auth.json stops freezing credentials behind a stale
``last_error_reset_at``. With *access_token* only the matching entry clears; otherwise every
rate-limited entry does (a redeemed banked reset restores the whole account; a still-exhausted
entry just re-freezes with fresh metadata on its next 429).
)Ú_borrowed_single_use_pool_rootÚ_profile_owns_pool_providerrÖ  r(   Nr§   rE   r8   z*Failed to clear Codex pool quota cooldownsTr_   )r´   rÛ  rÜ  rA   ry   rz   r¤   r—   r–   r   rJ   r«  rC   re   rf   rg   )
rE   rÛ  rÜ  ry   rz   r¤   ÚclearedÚtargetr�   r<   s
   &         r!   r�  r�  S  sì   € ÷ bßTÑTØ€GðRñ 5°^×DÒD‘ÑJhÓJjˆÙ¨&×1Ö1Ù)¨&Ó1ˆJÜ*¬=¸À^Ó+TÖU�ß¤C¨¯	©	°.Ó(A×(GÐ(GÀRÓ$HÈLÔ$XÙÜ1°%×8Ô8Ü,¨UÔ3Ø˜q•L’Gñ V÷ Ù  Õ@÷ 2ð €N÷ 2Ö1ð €Nûô ô RÜ�‰ÐAÈDˆÕQØ€NðRúsF   –C, ¤C, ¾ACÂCÂ%CÃ	CÃC, ÃC)	Ã#C, Ã)C, Ã,"DÄDc               ó    € V ^8„  d   QhRRRR/# )r   r˜   úOptional[list[Any]]r   zIterator[dict[str, Any]]r   )r    s   "r!   r"   r"   r  s   € ÷ ñ Ð2ð Ð7Oñ r#   c              #  ól   "  € T ;'       g    R F  p\        V\        4      '       g   K  Vx € K!  	  R # 5i)Nr   )rK   rd   )r˜   r<   s   & r!   r—   r—   r  s)   é € Ø—�˜B’ˆÜ�eœT×"Ô"ØŒKó ùs   ‚#4ª
4c               ó   € V ^8„  d   QhRR/# )r   r   rÐ  r   )r    s   "r!   r"   r"   x  s   € ÷ ñ Ð'?ñ r#   c                 ó�  € ^ RI Hp Hp ^ RIHp  \
        P
                  ! 4       p\        V! R4      4       Fã  pVP                  R4      pV ! V4      '       d   \        V4      '       g   K5  V! VP                  R4      4      pVe	   Wc8”  g   KX  RVP                  R4      RVP                  R4      R	VR
VP                  R4      RVP                  R4      RVP                  4       RVP                  R4      RVP                  R4      RVP                  R4      /	u # 	  R#   \         d    \        P                  RRR7        R# i ; i)zîReturn metadata for a pool-only Codex credential in quota cooldown.

Reads through ``read_credential_pool`` so a named profile with no Codex rows of its own sees
the global-root pool (the per-provider fallback every other pool read uses).©r€   Úread_credential_pool©Ú_parse_absolute_timestampr(   rE   Úlast_error_reset_atNrŸ   rp   r€  r®   r©  rÌ   rª  rˆ   rî   rl   z#Codex pool rate-limit lookup failedTr_   )rA   r€   rå  r´   rç  rM   r—   rJ   r«  r9   re   rf   rg   )r€   rå  rç  rÈ  r<   rƒ  r€  s          r!   r‹  r‹  x  s!  € ÷
 DÝ?ðKÜ�iŠi‹kˆÜ&Ñ';¸NÓ'KÖLˆEØ—I‘I˜nÓ-ˆEÙ  ×'Ò'Ô/MÈe×/TÒ/TÙÙ0°·±Ð;PÓ1QÓRˆHØÒ 8¦>à˜UŸY™Y wÓ/°ÀÇÁÈ>ÓAZØ ¨(°E·I±IÐ>QÓ4RØ˜uŸy™yÐ)=Ó>ÀÐPU×P[ÑP[ÓP]Ø# U§Y¡Y¨Ó%?ÀÀuÇyÁyÐQUÃØ §	¡	¨*Ó 5ð7ò 7ñ Mñ øô ô KÜ�‰Ð:ÀTˆÕJÙðKús   �A9D ÂBD ÄD Ä"EÅEc               ó$   € V ^8„  d   QhRRRRRR/# )r   r�   r=   Úprovider_idr   r   rà  r   )r    s   "r!   r"   r"   ’  s"   € ÷ :ñ :˜nð :¸3ð :ÐCVñ :r#   c                óª   € V P                  R4      p\        V\        4      '       d   VP                  V4      MRp\        V\        4      '       d   V# R# )zL``auth_store["credential_pool"][provider_id]`` when it is a list, else None.r  N)rJ   rK   rd   Úlist)r�   rê  Úpoolr˜   s   &&  r!   r–   r–   ’  sD   € à�>‰>Ð+Ó,€DÜ'1°$¼×'=Ò'=ˆd�h‰h�{Ô#À4€GÜ  ¬$×/Ò/ˆ7Ð9°TÐ9r#   c               ó   € V ^8„  d   QhRR/# )r   r   rÆ   r   )r    s   "r!   r"   r"   ™  s   € ÷ ñ  ñ r#   c                 óÆ  € ^ RI Hp  ^ RIHpHp  \        V! R4      4       F“  pVP                  R4      pV ! VP                  R4      4      pVRJ;'       d    V\        P                  ! 4       8„  pV! V4      '       g   K_  V'       d   Ki  VP                  4       \        VP                  R4      4      3u # 	  R#   \         d    \        P                  RR	R
7        R# i ; i)a—  ``(access_token, row base_url)`` of the first pool entry with a non-empty access_token that is
not in an exhaustion cooldown window, so the caller routes the token to the host that row belongs
to; ``("", "")`` when none is usable.

Fallback for ``resolve_codex_runtime_credentials`` when the singleton has no creds; reads
through ``read_credential_pool`` so a profile inherits the global-root pool (#34143).ræ  rä  r(   rE   rè  Nrl   z!Codex pool fallback lookup failedTr_   )r8   r8   )r´   rç  rA   r€   rå  r—   rJ   rM   r9   r:   re   rf   rg   )rç  r€   rå  r<   rƒ  r€  Úin_cooldowns          r!   rˆ  rˆ  ™  sÆ   € õ @ßCð
IÜ&Ñ';¸NÓ'KÖLˆEØ—I‘I˜nÓ-ˆEñ 1°·±Ð;PÓ1QÓRˆHØ"¨$Ð.×IÐI°8¼d¿iºi»kÑ3IˆKÙ˜U×#Ô#¯K©KØ—{‘{“}¤i°·	±	¸*Ó0EÓ&FÐFÒFñ Mð €Møô ô IÜ�‰Ð8À4ˆÕHØ€MðIús*   �AB: Á$B: Â B: Â
+B: Â6B: Â:"C ÃC Úforce_new_loginc               ó$   € V ^8„  d   QhRRRRRR/# )r   Úpconfigr   rñ  rG   r   r>   r   )r    s   "r!   r"   r"   °  s'   € ÷ %Lñ %L ~ð %LÈ4ð %LÐ\`ñ %Lr#   c          	     ó0  € ^ RI HpHpHpHpHpHpHp	Hp
 ^ RI	H
p ?V'       gš   V! RV
VR\        RR7      '       d   R# V! 4       pV'       dq   \        R4       \        R	4       V! R
RR7      '       dK   V! V4       V	! R\        4       4      p\        4        \        R4       \        R4       \        RV R24       R# \        4        V! V 4      pV! VR,          VP                  R4      4       V	! RVP                  R\        4      4      pV! RVRR7       R# )z™OpenAI Codex login: device code by default, browser PKCE when opted in (``--browser`` /
``auth.codex_login_flow``). Tokens stored in ~/.hermes/auth.json.)rO   r³   Ú!_offer_existing_oauth_credentialsÚ_print_login_successÚ_prompt_yes_nor¬   Ú_update_config_for_providerr•  )Úcodex_oauth_loginr(   ÚCodexz?Existing Codex credentials are expired. Starting fresh login...)ÚresolveÚis_expiringÚdisplay_nameÚdefault_base_urlÚexpired_noticeNz:Found existing Codex CLI credentials at ~/.codex/auth.jsonzOHermes will create its own session to avoid conflicts with Codex CLI / VS Code.zCImport these credentials? (a separate login is recommended) [y/N]: Ún)Údefaultz=Credentials imported. Note: if Codex CLI refreshes its token,z<Hermes will keep working independently with its own session.z  Config updated: z (model.provider=openai-codex)r„   rp   rl   T)Úshow_auth_state)rA   rO   r³   rõ  rö  r÷  r¬   rø  r•  Úhermes_cli.auth_codex_browserrù  r   ÚprintrW   rJ   )Úargsró  rñ  rO   r³   rõ  rö  r÷  r¬   rø  r•  rù  Ú
cli_tokensÚconfig_pathÚcredss   &&$            r!   Ú_login_openai_codexr	  °  s  € ÷H÷ Hó Hõ @ØßÙ,ØÐ$EØ7ÀgÜ3Ø\÷	^ó ^ñ
 Ù-Ó/ˆ
ßÜÐNÔOÜÐcÔdÙØUÐ_b÷dó dá" :Ô.Ù9¸.Ì/ÓJ[Ó\�Ü”ÜÐUÔVÜÐTÔUÜÐ*¨;¨-Ð7UÐVÔWÙô 
„GÙ˜dÓ#€EÙ�u˜X•¨¯	©	°.Ó(AÔBÙ-Ø˜Ÿ	™	 *Ô.DÓEóG€Ká˜¨Àd×Kr#   Úduringc               ó$   € V ^8„  d   QhRRRRRR/# )r   rÃ   rÄ   r
  r   r   r   r   )r    s   "r!   r"   r"   Ø  s"   € ÷ !ñ !¨nð !Èð !ÐV_ñ !r#   c               ór   € \        \        V RR4      4      pVe   RV R2MRp\        RV RV 2\        4      # )zYAuthError for a 429 from OpenAI's device-auth endpoints (throttle, not credential fault).r,   Nz Try again in about zs.z' Wait a minute and run the login again.z7OpenAI is rate-limiting Codex login requests (HTTP 429)zJ. This is a temporary throttle on OpenAI's side, not a credential problem.)r3   r)  r   r   )rÃ   r
  r7  Ú	wait_hints   &$  r!   Ú_codex_login_rate_limited_errorr  Ø  s]   € ô -¬W°X¸yÈ$Ó-OÓP€Kà2=Ò2IÐ
˜{˜m¨2Ñ.Ø6ð ô Ø
AÀ&Àð JSØS\ÐR]ð	_äó!ð !r#   c               ó$   € V ^8„  d   QhRRRRRR/# )r   Úissuerr   rV  r   r=   r   )r    s   "r!   r"   r"   è  s!   € ÷ ñ  sð °sð ¸~ñ r#   c           
     óä  € ^p\        ^V^,           4       F›  p\        V  R2RV/RR/RR7      pVP                  R8w  d    MsW28  g   K4  \        \	        VRR4      4      p\        ^\        \        Ve   TM^V,          4      ^<4      4      p\        R	V R
24       \        P                  ! V4       K�  	  XP                  R8X  d   \        V4      hVP                  ^È8w  d   \        RVP                   R2R4      hVP                  4       p\        ^\        VP                  RR4      4      4      VR&   VP                  RR4      '       d   VP                  RR4      '       g   \        RR4      hV# )u  Step 1 of the Codex device flow: request a user code, retrying capped on HTTP 429.

OpenAI rate-limits this request when login is attempted too often from one IP/account â€” retry
with capped backoff (honoring ``Retry-After``) before surfacing an actionable message.
z!/api/accounts/deviceauth/usercoderV  rS  rQ  )rÓ   r,   rö   rX  r,   Nz:OpenAI is rate-limiting login requests (429); retrying in zs...z$Device code request returned status r=  Údevice_code_request_errorÚintervalÚ5Ú	user_coder8   Údevice_auth_idz-Device code response missing required fields.Údevice_code_incomplete)zFailed to request device codeÚdevice_code_request_failed)Úranger  rF  r3   r)  rN   Úminr1   r  rM   rÿ   r  r   rÓ   rJ   )r  rV  Úmax_attemptsr   Úrespr7  ÚdelayÚdevice_datas   &&      r!   Ú_codex_request_device_coder  è  sj  € ð €LÜ˜˜L¨1Õ,Ö-ˆÜ ØˆhÐ7Ð8ÀÈYÐ?WØ#Ð%7Ð8ØSôUˆð ×Ñ˜sÔ"ÙØÖ!ä4´W¸TÀ9ÈdÓ5SÓTˆKÜ˜œ3œs°+Ò2I¡;ÈqÐT[Í|Ó\Ð^`ÓaÓbˆEÜÐNÈuÈgÐUYÐZÔ[Ü�JŠJ�uÖñ .ð ×Ñ˜3ÔÜ-¨dÓ3Ð3Ø×Ñ˜3ÔÜØ2°4×3CÑ3CÐ2DÀAÐFÐHcóeð 	eà—)‘)“+€KÜ! !¤S¨¯©¸ÀSÓ)IÓ%JÓK€K�
ÑØ�?‰?˜;¨×+Ò+°;·?±?ÐCSÐUW×3XÒ3XÜÐHÐJbÓcÐcØÐr#   c          
     ó,   € V ^8„  d   QhRRRRRRRRRR/# )	r   r  r   r  r  Úpoll_intervalr1   r   r=   r   )r    s   "r!   r"   r"     s4   € ÷ +ñ +Øð+Ø$'ð+Ø47ð+ØHKð+ØP^ñ+r#   c               ó|  € Rp^p\         P                  ! 4       pRp \        \        P                  ! R4      R7      ;_uu_ 4       p^ p	\         P                  ! 4       V,
          V8  d†   \         P
                  ! V4        VP                  V  R2RVRV/RR/R	7      p
^ p	T
P                  ^È8X  d   T
P                  4       pM-T
P                  R9  g   KŒ  \        RT
P                   R2R4      hRRR4       Tf   \        RR4      hT#   \         dq   p\        T4      '       g   \        R
T \        T4       2R4      ThT	^,          p	Y•8¼  d   \        RT	 RT \        T4       2R4      Th\        R4        Rp?EK6  Rp?ii ; i  + '       g   i     L ; i  \         d    \        R4       \        ^‚4      hi ; i)zXStep 3 of the Codex device flow: poll until sign-in completes (403/404 = still pending).Nrù   rú   z/api/accounts/deviceauth/tokenr  r  rS  rQ  )rÓ   r,   z$Device auth polling request failed: Údevice_code_poll_errorz)Device auth polling request failed after z consecutive transport errors: z>Transient network error while waiting for sign-in; retrying...z$Device auth polling returned status r=  z
Login cancelled.z!Login timed out after 15 minutes.Údevice_code_timeouti„  >   rE  é”  )rM   rÄ  rü   r   rý   rÿ   rþ   re   ró   r   rê   r  rF  rÓ   ÚKeyboardInterruptÚ
SystemExit)r  r  r  r!  Úmax_waitÚmax_consecutive_blipsÚstartÚ	code_respr  Úconsecutive_blipsÚ	poll_resprÒ   s   &$$$        r!   Ú_codex_poll_authorization_coder.    sá  € ð €HØÐÜ�NŠNÓ€EØ€Ið!Ü¬¯ª°dÓ(;×<Õ<ÀØ !ÐÜ—.’.Ó" UÕ*¨XÔ5Ü—
’
˜=Ô)ðØ &§¡Ø!˜(Ð"@ÐAØ.°ÀÈYÐWØ!/Ð1CÐ Dð !,ó !F�Ið" %&Ð!Ø×(Ñ(¨CÔ/Ø )§¡Ó 0�IØØ×(Ñ(°
Ö:Ü$Ø>¸y×?TÑ?TÐ>UÐUVÐWØ0ó2ð 2÷7 =ðB ÒÜÐ<Ð>SÓTÐTØÐøô5 !ô Ü8¸×=Ò=Ü(ØBÀ3À%ÔHYÐZ]ÓH^ÐG_Ð`Ø4ó6à;>ð?ð &¨Õ*Ð%Ø(ÔAÜ(ØGÐHYÐGZð [2Ø25°Ô7HÈÓ7MÐ6NðPà4ó6ð <?ð?ô ÐZÔ[Þûðú÷ =×<ûô< ô ÜÐ"Ô#Ü˜‹oÐðúsZ   �(F Á:FÂ DÂ2FÃFÃ.F ÄFÄA$E>Å7FÅ>FÆFÆF	ÆF ÆF Æ"F;c               ó(   € V ^8„  d   QhRRRRRRRR/# )r   r  r   rV  r+  r=   r   r   )r    s   "r!   r"   r"   6  s,   € ÷ ñ ØðØðØ,:ðØ?Mñr#   c                óÄ  € VP                  RR4      pVP                  RR4      pV'       d	   V'       g   \        RR4      h\        \        RRRVRV  R	2R
VRV/RR/RR7      pVP                  R8X  d   \        VRR7      hVP                  ^È8w  d   \        RVP                   R2R4      hVP                  4       pVP                  RR4      '       g   \        RR4      hV# )zHStep 4 of the Codex device flow: swap the authorization code for tokens.Úauthorization_coder8   Úcode_verifierzADevice auth response missing authorization_code or code_verifier.Údevice_code_incomplete_exchangerU  rÍ   Úredirect_uriz/deviceauth/callbackrV  rS  rT  )rW  r,   rö   rX  z during token exchange)r
  zToken exchange returned status r=  Útoken_exchange_errorrE   z.Token exchange did not return an access_token.Útoken_exchange_no_access_token)zToken exchange failedÚtoken_exchange_failed)rJ   r   r  r   rF  r  rÓ   )r  rV  r+  r1  r2  Ú
token_respr„   s   &&&    r!   Ú"_codex_exchange_authorization_coder9  6  s
  € ð #Ÿ™Ð';¸RÓ@ÐØ—M‘M /°2Ó6€Mß§]ÜØOØ-ó/ð 	/ô #ÜàÐ.°Ð8JØ˜v˜hÐ&:Ð;¸[È)Ø˜]ð,ð  Ð!DÐEØBôD€Jð ×Ñ Ô$Ü-¨jÐAYÔZÐZØ×Ñ Ô$ÜØ-¨j×.DÑ.DÐ-EÀQÐGÐI_óað 	aà�_‰_Ó€FØ�:‰:�n b×)Ò)ÜØ<Ð>^ó`ð 	`à€Mr#   c               ó   € V ^8„  d   QhRR/# )r   r   r=   r   )r    s   "r!   r"   r"   S  s   € ÷ !ñ ! .ñ !r#   c            
     ó¢  € ^ RI Hp  R\        r!\        W4      pVR,          p\	        R4       \	        R4       \	        RV R24       \	        R4       \	        RV R	24       \	        R
4       \        WR,          VVR,          R7      p\        WV4      pRRVP                  RR4      RVP                  RR4      /R\        4       RV ! 4       RRRR/# )zBRun the OpenAI device code login flow and return credentials dict.)r¦   zhttps://auth.openai.comr  z!To continue, follow these steps:
z#  1. Open this URL in your browser:z
     [94mz/codex/device[0m
z  2. Enter this code:z[0m
z/Waiting for sign-in... (press Ctrl+C to cancel)r  r  )r  r  r!  r„   rE   r8   rˆ   rl   rp   rs   rt   ro   zdevice-code)	rA   r¦   r   r  r  r.  r9  rJ   rW   )r¦   r  rV  r  r  r+  r„   s          r!   Ú_codex_device_code_loginr<  S  sã   € å*Ø1Ô3HˆIÜ,¨VÓ?€KØ˜KÕ(€Iô 
Ð
.Ô/Ü	Ð
/Ô0Ü	ˆM˜&˜Ð!7Ð
8Ô9Ü	Ð
!Ô"Ü	ˆM˜)˜ IÐ
.Ô/Ü	Ð
;Ô<Ü.ØÐ+;Õ<È	Ø! *Õ-ô/€Iô 0°À9ÓM€Fð 	Ø˜FŸJ™J ~°rÓ:Ø˜VŸZ™Z¨¸Ó<ð>ð 	”OÓ% ~±z³|À[ÐR[Ø�-ð!ð !r#   r7   rÜ   )NN)z[SSL:z_ssl.cÚUNEXPECTED_EOFi   )_Ú__conditional_annotations__r&  Ú
__future__r   Úloggingr°  rÓ   rT   Ú	threadingrM   Ú
contextlibr   Úpathlibr   Útypingr   r   r   r   r	   r
   r   Úhermes_cli.auth_constantsr   r   r   r   r   r   r   r   r   r   r   Úutilsr   r   rA   r   Ú	getLoggerrf   r‹   rŒ   rŠ   r*   r3   r:   rC   rO   rW   rk   ru   r{   r�   r›   r¬   r¾   rÖ   rä   rê   ró   r  r  r*  r/  rü   r9  rK  r_  rj  r³   r•  r£  r«  Ú&CODEX_QUOTA_PROBE_MIN_INTERVAL_SECONDSr¬  Ú__annotations__ÚLockrÅ  r´  rº  r|  rÓ  rŒ  r�  r—   r‹  r–   rˆ  r	  r  r  r.  r9  r<  )r>  s   @r!   Ú<module>rK     sF  øðôõ #ã Û Û Û 	Û Û Ý Ý ß L× LÑ L÷`÷ `÷ `ñ `÷ 6çÝ.ð 
×	Ò	Ð,Ó	-€ð fÐ ØgÐ ØUÐ õ1õ5õ$õ#õeõ
`÷.ð*PØY]÷PõðI¨÷ I÷,&(ñR(@àð(@à38÷(@ð (@÷V'ðTð "ðð KOðð ÷	ðB =Ð õõ4õ"ð( )Ð õ%õP<õõ,Gõ$?ðN%ØGK÷%õP*õZð,fYØ"ðfYØ@DðfYà GðfYð ÷fYõRPõ)ð *-Ð &ØDFÐ ÐAÓ FØ#Ÿ.š.Ó*Ð õBõ
ð6Ø48ð6à"H÷6ðr"à"H÷"õJj÷.õ>õõ4:õð.%LÐRW÷ %LðP!Èr÷ !õ õ@+õ\÷:!r#   