
    i                   p
   U 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Zddl	Z	ddl
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 ddlmZmZ ddlmZ ddlmZmZmZmZ ddlZddlZdd	l m!Z!m"Z"m#Z# dd
l$m%Z%  ej&        e'          Z(	 ddl)Z)n# e*$ r dZ)Y nw xY w	 ddl+Z+n# e*$ r dZ+Y nw xY wdZ,dZ-dZ.dZ/dZ0dZ1dZ2dZ3dZ4dZ5dZ6dZ7dZ8dZ9dZ:dZ;dZ<dZ=dZ>dZ?dZ@dZAe G d d                      ZBi d  eBd d!d"e.e/e0e1#          d$ eBd$d%d&e5'          d( eBd(d)d&e6'          d* eBd*d+d&e@'          d, eBd,d-d.e7d/d01          d2 eBd2d3d4e8d56          d7 eBd7d8d.d9d:d;1          d< eBd<d=d.d>d?d@1          dA eBdAdBd.dCdDdE1          dF eBdFdGd.dHdIJ          dK eBdKdLd.dMdNdO1          dP eBdPdQd.dRdSdT1          dU eBdUdVd.dWdXJ          dY eBdYdZd.d[d\d]1          d^ eBd^d_d.d`dadb1          dc eBdcddd.dedfdg1          dh eBdhdid.djdkdl1           eBdmdnd.dodpdq1           eBdrdsd.dtdudv1           eBdwdxd.dydzd{1           eBd|d}d.d~dd1           eBddd.ddd1           eBddd.ddd1           eBddd.e9dd1           eBdddddd1          dZCdeDd<   dddZEdZFdedZGh dZHdddfdZIdgdZJdd>dgdfdddgdfddg ddfddg ddfgZKdhdidZLdedZM G d deN          ZOdjdÄZPdkdƄZQdldǄZRddȜdmd̈́ZSdndτZTdndЄZU ejV                    ZWee-fdod҄            ZXdpdqdքZYdrd؄ZZdsdڄZ[dtd܄Z\dpdud݄Z]dvdZ^dwdZ_dxdZ`dydZadzdZbd{dZcdpd|dZdd}dZed~dZf	 dpdddddZgddZhddZiddZjddZkddZlddZmdndZnddZoddZpe?fdd ZqdddZrdde?ddd	Zsdd
ZtddddZuddZvdldZwddddZxddddZydpddZzddddZ{ddZ|ddZ}dde<dddZ~dddddd#Zdd)Zdd,Zdd-Zdd/Zddd0dd4Zdd5Zddde3d6dd7Zd8e1dddde2dddddd9ddAZe2ddddBddCZe2dddddDddEZddFZddGZddHZddIZdpdudJZddKZddLZ	 dpddNZdndOZ	 	 	 	 dddXZddZZd}d[Zdd\Zdd]Zddddddddd^d_	ddaZddbZd}dcZdS (  aZ  
Multi-provider authentication system for Hermes Agent.

Supports OAuth device code flows (Nous Portal, future: OpenAI Codex) and
traditional API key providers (OpenRouter, custom endpoints). Auth state
is persisted in ~/.hermes/auth.json with cross-process file locking.

Architecture:
- ProviderConfig registry defines known OAuth providers
- Auth store (auth.json) holds per-provider credential state
- resolve_provider() picks the active provider via priority chain
- resolve_*_runtime_credentials() handles token refresh and key minting
- logout_command() is the CLI entry point for clearing auth
    )annotationsN)contextmanager)	dataclassfield)datetimetimezone)Path)AnyDictListOptional)get_hermes_homeget_config_pathread_raw_config)OPENROUTER_BASE_URL         .@zhttps://portal.nousresearch.comz)https://inference-api.nousresearch.com/v1
hermes-clizinference:mint_agent_keyi  x   z%https://chatgpt.com/backend-api/codexzhttps://portal.qwen.ai/v1zhttps://api.githubcopilot.comzacp://copilotzhttps://ollama.com/v1app_EMoamEEZ73f0CkXaXp7hrannz#https://auth.openai.com/oauth/token f0304373b74a44d2b584a3fb70ca9e56z(https://chat.qwen.ai/api/v1/oauth2/tokenzcloudcode-pa://google<   c                      e Zd ZU dZded<   ded<   ded<   dZded<   dZded<   dZded	<   dZded
<    e	e
          Zded<   dZded<   dZded<   dS )ProviderConfigz%Describes a known inference provider.stridname	auth_type portal_base_urlinference_base_url	client_idscope)default_factoryDict[str, Any]extra tupleapi_key_env_varsbase_url_env_varN)__name__
__module____qualname____doc____annotations__r    r!   r"   r#   r   dictr&   r)   r*   r'       7/home/agentuser/.hermes/hermes-agent/hermes_cli/auth.pyr   r   Z   s         //GGGIIINNNO     IEOOOO!E$777E7777     r1   r   nouszNous Portaloauth_device_code)r   r   r   r    r!   r"   r#   openai-codexzOpenAI Codexoauth_external)r   r   r   r!   
qwen-oauthz
Qwen OAuthgoogle-gemini-clizGoogle Gemini (OAuth)copilotzGitHub Copilotapi_key)COPILOT_GITHUB_TOKENGH_TOKENGITHUB_TOKENCOPILOT_API_BASE_URL)r   r   r   r!   r)   r*   copilot-acpzGitHub Copilot ACPexternal_processCOPILOT_ACP_BASE_URL)r   r   r   r!   r*   geminizGoogle AI Studioz7https://generativelanguage.googleapis.com/v1beta/openai)GOOGLE_API_KEYGEMINI_API_KEYGEMINI_BASE_URLzaiz
Z.AI / GLMzhttps://api.z.ai/api/paas/v4)GLM_API_KEYZAI_API_KEYZ_AI_API_KEYGLM_BASE_URLkimi-codingzKimi / Moonshotzhttps://api.moonshot.ai/v1)KIMI_API_KEYKIMI_BASE_URLkimi-coding-cnzKimi / Moonshot (China)zhttps://api.moonshot.cn/v1)KIMI_CN_API_KEY)r   r   r   r!   r)   arceezArcee AIzhttps://api.arcee.ai/api/v1)ARCEEAI_API_KEYARCEE_BASE_URLminimaxMiniMaxz https://api.minimax.io/anthropic)MINIMAX_API_KEYMINIMAX_BASE_URL	anthropic	Anthropiczhttps://api.anthropic.com)ANTHROPIC_API_KEYANTHROPIC_TOKENCLAUDE_CODE_OAUTH_TOKENalibabazAlibaba Cloud (DashScope)z6https://dashscope-intl.aliyuncs.com/compatible-mode/v1)DASHSCOPE_API_KEYDASHSCOPE_BASE_URL
minimax-cnzMiniMax (China)z"https://api.minimaxi.com/anthropic)MINIMAX_CN_API_KEYMINIMAX_CN_BASE_URLdeepseekDeepSeekzhttps://api.deepseek.com/v1)DEEPSEEK_API_KEYDEEPSEEK_BASE_URLxaixAIzhttps://api.x.ai/v1)XAI_API_KEYXAI_BASE_URL
ai-gatewayzVercel AI Gatewayzhttps://ai-gateway.vercel.sh/v1)AI_GATEWAY_API_KEYAI_GATEWAY_BASE_URLopencode-zenzOpenCode Zenzhttps://opencode.ai/zen/v1)OPENCODE_ZEN_API_KEYOPENCODE_ZEN_BASE_URLopencode-gozOpenCode Gozhttps://opencode.ai/zen/go/v1)OPENCODE_GO_API_KEYOPENCODE_GO_BASE_URLkilocodez	Kilo Codezhttps://api.kilo.ai/api/gateway)KILOCODE_API_KEYKILOCODE_BASE_URLhuggingfacezHugging Facez https://router.huggingface.co/v1)HF_TOKENHF_BASE_URLxiaomizXiaomi MiMozhttps://api.xiaomimimo.com/v1)XIAOMI_API_KEYXIAOMI_BASE_URLollama-cloudzOllama Cloud)OLLAMA_API_KEYOLLAMA_BASE_URLbedrockzAWS Bedrockaws_sdkz/https://bedrock-runtime.us-east-1.amazonaws.comr'   BEDROCK_BASE_URL)rj   rm   rp   rs   rv   ry   r|   r   zDict[str, ProviderConfig]PROVIDER_REGISTRYreturnr   c                     ddl m}  t          d         j        D ](} | |          pt	          j        |d          }|r|c S )dS )aQ  Return the first usable Anthropic credential, or ``""``.

    Checks both the ``.env`` file (via ``get_env_value``) and the process
    environment (``os.getenv``).  The fallback order mirrors the
    ``PROVIDER_REGISTRY["anthropic"].api_key_env_vars`` tuple:

        ANTHROPIC_API_KEY -> ANTHROPIC_TOKEN -> CLAUDE_CODE_OAUTH_TOKEN
    r   )get_env_valuerW   r   )hermes_cli.configr   r   r)   osgetenv)r   varvalues      r2   get_anthropic_keyr   7  sh     0///// ->  c""8biR&8&8 	LLL	2r1   zhttps://api.kimi.com/coding/v1default_urlenv_overridec                F    |r|S |                      d          rt          S |S )zReturn the correct Kimi base URL based on the API key prefix.

    If the user has explicitly set KIMI_BASE_URL, that always wins.
    Otherwise, sk-kimi- prefixed keys route to api.kimi.com/coding/v1.
    zsk-kimi-)
startswithKIMI_CODE_BASE_URL)r:   r   r   s      r2   _resolve_kimi_base_urlr   T  s4      *%% "!!r1   >   *****your-api-key*nonenulldummyexamplechangemeplaceholderyour_api_key   )
min_lengthr   r
   r   intboolc                   t          | t                    sdS |                                 }t          |          |k     rdS |                                t
          v rdS dS )zIReturn True when a configured secret looks usable, not empty/placeholder.FT)
isinstancer   striplenlower_PLACEHOLDER_SECRET_VALUES)r   r   cleaneds      r2   has_usable_secretr   q  sZ    eS!! ukkmmG
7||j  u}}444u4r1   provider_idpconfigtuple[str, str]c                R   | dk    rZ	 ddl m}  |            \  }}|r||fS n=# t          $ r%}t                              d|           Y d}~nd}~wt
          $ r Y nw xY wdS |j        D ]>}t          j        |d          	                                }t          |          r||fc S ?dS )zDResolve an API-key provider's token and indicate where it came from.r9   r   )resolve_copilot_tokenz#Copilot token validation failed: %sN)r   r   r   )hermes_cli.copilot_authr   
ValueErrorloggerwarning	Exceptionr)   r   r   r   r   )r   r   r   tokensourceexcenv_varvals           r2    _resolve_api_key_provider_secretr   }  s    i	EEEEEE1133ME6 %f}$% 	G 	G 	GNN@#FFFFFFFF 	 	 	D	v+    i$$**,,S!! 	 <	  6s   " 
AAAAglobalzglm-5Globalcnz$https://open.bigmodel.cn/api/paas/v4Chinazcoding-globalz#https://api.z.ai/api/coding/paas/v4)zglm-5.1zglm-5v-turbozglm-4.7zGlobal (Coding Plan)z	coding-cnz+https://open.bigmodel.cn/api/coding/paas/v4zChina (Coding Plan)       @timeoutfloatOptional[Dict[str, str]]c                   t           D ]\  }}}}|D ]}	 t          j        | dd|  dd|ddddd	gd
|          }|j        dk    r(t                              d|||           ||||dc c S t                              d|||j                   # t          $ r'}t                              d|||           Y d}~d}~ww xY wdS )a+  Probe z.ai endpoints to find one that accepts this API key.

    Returns {"id": ..., "base_url": ..., "model": ..., "label": ...} for the
    first working endpoint, or None if all fail.  For endpoints with multiple
    candidate models, tries each in order and returns the first that succeeds.
    z/chat/completionsBearer application/json)AuthorizationContent-TypeFr   userping)rolecontent)modelstream
max_tokensmessages)headersjsonr      z(Z.AI endpoint probe: %s (%s) model=%s OK)r   base_urlr   labelz,Z.AI endpoint probe: %s model=%s returned %sz+Z.AI endpoint probe: %s model=%s failed: %sN)ZAI_ENDPOINTShttpxpoststatus_coder   debugr   )	r:   r   ep_idr   probe_modelsr   r   respr   s	            r2   detect_zai_endpointr     sn    1> _ _,xu! 	_ 	_E_z222)<7)<)<(: 
 "'"'&'.4%H%H$I	  $   #s**LL!KUT\^cddd#$,!&!&	       KUTY[_[kllll _ _ _JESXZ]^^^^^^^^_3	_6 4s   AB1"B
CC  Cc                   |r|S t                      }t          |d          pi }|                    d          }t          |t                    r|                    d          r|                    dd          }|t          j        |                                                                           dd         k    r)t          
                    d|d                    |d         S t          |           }|r|                    d          rt          j        |                                                                           dd         }|d         |                    d	d          |                    d
d          |                    dd          |d|d<   t          |d|           t                              d|d         |d                    |d         S t          
                    d|           |S )aY  Return the correct Z.AI base URL by probing endpoints.

    If the user has explicitly set GLM_BASE_URL, that always wins.
    Otherwise, probe the candidate endpoints to find one that accepts the
    key.  The detected endpoint is cached in provider state (auth.json) keyed
    on a hash of the API key so subsequent starts skip the probe.
    rF   detected_endpointr   key_hashr   N   zZ.AI: using cached endpoint %sr   r   r   )r   endpoint_idr   r   r   z$Z.AI: auto-detected endpoint %s (%s)z.Z.AI: probe failed, falling back to default %s)_load_auth_store_load_provider_stategetr   r0   hashlibsha256encode	hexdigestr   r   r   _save_provider_stateinfo)r:   r   r   
auth_storestatecachedr   detecteds           r2   _resolve_zai_base_urlr     s      "##J U339rEYY*++F&$ &FJJz$:$: &::j"--w~gnn&6&677AACCCRCHHHLL96*;MNNN*%% #7++H $HLL,, $>'.."2"233==??D ,#<<b11\\'2..\\'2.. &
 &
!" 	Z666:HW<MxXbOcddd
##
LLA;OOOr1   c                  .     e Zd ZdZddddd fdZ xZS )	AuthErrorz,Structured auth error with UX mapping hints.r   NFprovidercoderelogin_requiredmessager   r   r   Optional[str]r   r   r   Nonec               t    t                                          |           || _        || _        || _        d S N)super__init__r   r   r   )selfr   r   r   r   	__class__s        r2   r   zAuthError.__init__   s9     	!!! 	 0r1   )
r   r   r   r   r   r   r   r   r   r   )r+   r,   r-   r.   r   __classcell__)r   s   @r2   r   r     sX        66 "!&1 1 1 1 1 1 1 1 1 1 1 1r1   r   errorr   c                    t          | t                    st          |           S | j        r|  dS | j        dk    r	 dS | j        dk    r	 dS | j        dk    r|  dS t          |           S )z2Map auth failures to concise user-facing guidance.z' Run `hermes model` to re-authenticate.subscription_requiredzfNo active paid subscription found on Nous Portal. Please purchase/activate a subscription, then retry.insufficient_creditszTSubscription credits are exhausted. Top up/renew credits in Nous Portal, then retry.temporarily_unavailablez Please retry in a few seconds.)r   r   r   r   r   )r   s    r2   format_auth_errorr    s    eY'' 5zz A@@@@z,,,C	
 	

 z+++?	
 	

 z...8888u::r1   r   r   c                    t          | t                    sdS |                                 }|sdS t          j        |                    d                                                    dd         S )zJReturn a short hash fingerprint for telemetry without leaking token bytes.Nutf-8   )r   r   r   r   r   r   r   )r   r   s     r2   _token_fingerprintr  (  sd    eS!! tkkmmG t>'..1122<<>>ssCCr1   c                 |    t          j        dd                                                                          } | dv S )NHERMES_OAUTH_TRACEr   >   1onyestrue)r   r   r   r   )raws    r2   _oauth_trace_enabledr  2  s8    
)("
-
-
3
3
5
5
;
;
=
=C,,,r1   sequence_ideventr  fieldsr   c                   t                      sd S d| i}|r||d<   |                    |           t                              dt	          j        |dd                     d S )Nr  r  zoauth_trace %sTF)	sort_keysensure_ascii)r  updater   r   r   dumps)r  r  r  payloads       r2   _oauth_tracer  7  sp    !! &.G -!,NN6
KK $*WSX"Y"Y"YZZZZZr1   r	   c                 $    t                      dz  S )N	auth.json)r   r'   r1   r2   _auth_file_pathr  E  s    {**r1   c                 D    t                                          d          S )Nz.lock)r  with_suffixr'   r1   r2   _auth_lock_pathr!  I  s    ((111r1   timeout_secondsc              #    K   t          t          dd          dk    rLt          xj        dz  c_        	 dV  t          xj        dz  c_        n# t          xj        dz  c_        w xY wdS t                      }|j                            dd           t          8t          1dt          _        	 dV  dt          _        n# dt          _        w xY wdS t          rH|                                r|	                                j
        dk    r|                    dd	           |                    t          rd
nd          5 }t          j                    t          d|           z   }	 	 t          r?t          j        |                                t          j        t          j        z             nG|                    d           t          j        |                                t          j        d           nX# t,          t.          t0          f$ r= t          j                    |k    rt3          d          t          j        d           Y nw xY wdt          _        	 dV  dt          _        t          r3t          j        |                                t          j                   nt          r`	 |                    d           t          j        |                                t          j        d           n# t.          t:          f$ r Y nw xY wn# dt          _        t          r2t          j        |                                t          j                   w t          r`	 |                    d           t          j        |                                t          j        d           w # t.          t:          f$ r Y w w xY ww xY wddd           dS # 1 swxY w Y   dS )zCCross-process advisory lock for auth.json reads+writes.  Reentrant.depthr   r   NTparentsexist_ok r  encodingzr+za+g      ?z%Timed out waiting for auth store lockg?)getattr_auth_lock_holderr$  r!  parentmkdirfcntlmsvcrtexistsstatst_size
write_textopentimemaxflockfilenoLOCK_EXLOCK_NBseeklockingLK_NBLCKBlockingIOErrorOSErrorPermissionErrorTimeoutErrorsleepLOCK_UNLK_UNLCKIOError)r"  	lock_path	lock_filedeadlines       r2   _auth_store_lockrJ  O  s       '1--111$	)EEE##q(#####q(######!!I4$777}"#	(EEE&'##a#''''  4y'')) 4Y^^-=-=-E-J-JS7333	0D	1	1 Y9;;S/!:!::	!
! KK	 0 0 2 2EMEM4QRRRRNN1%%%N9#3#3#5#5vJJJ#Wo> ! ! !9;;(**&'NOOO
4     !	! #$	EEE&'# I,,..>>>> NN1%%%N9#3#3#5#5vJJJJ)   D	 '(# I,,..>>>> NN1%%%N9#3#3#5#5vJJJJ)   D	-                 s   A A$.B? ?C?&O	&BG43O	4AIO	IO	LAO	+AK32O	3LO	LO	AN9AN! N9!N5	2N94N5	5N99O		OO	auth_fileOptional[Path]r%   c                   | pt                      } |                                 s
t          i dS 	 t          j        |                                           }n# t          $ r t          i dcY S w xY wt          |t                    rht          |	                    d          t                    s(t          |	                    d          t                    r|
                    di            |S t          |t                    rPt          |	                    d          t                    r(|d         }i }d|v r|d         |d<   t          ||rdnd dS t          i dS )N)version	providersrO  credential_poolsystemsnous_portalr3   )rN  rO  active_provider)r  r1  AUTH_STORE_VERSIONr   loads	read_textr   r   r0   r   
setdefault)rK  r  rQ  rO  s       r2   r   r     s   ._..I @-B???@j,,..// @ @ @-B?????@ #t 377;''..cgg/00$77 	{B'''
 #t BCGGI,>,>!E!E Bi.	G## ' 6If-I-6#@66DB B 	B *;;;s   &A A.-A.r   c                B   t                      }|j                            dd           t          | d<   t	          j        t          j                                                  | d<   t          j
        | d          dz   }|                    |j         dt          j                     d	t          j                    j                   }	 |                    d
d          5 }|                    |           |                                 t          j        |                                           d d d            n# 1 swxY w Y   t          j        ||           	 t          j        t1          |j                  t          j                  }n# t4          $ r d }Y nw xY w|C	 t          j        |           t          j        |           n# t          j        |           w xY w	 |                                r|                                 nO# t4          $ r Y nCw xY w# 	 |                                r|                                 w w # t4          $ r Y w w xY wxY w	 |                    t>          j         t>          j!        z             n# t4          $ r Y nw xY w|S )NTr%  rN  
updated_at   indent
z.tmp..wr  r)  )"r  r-  r.  rT  r   nowr   utc	isoformatr   r  	with_namer   r   getpiduuiduuid4hexr5  writeflushfsyncr9  replacer   O_RDONLYr@  closer1  unlinkchmodr2  S_IRUSRS_IWUSR)r   rK  r  tmp_pathhandledir_fds         r2   _save_auth_storeru    s   !!I4$777.Jy'|HL99CCEEJ|jA...5G""in#[#[29;;#[#[IY#[#[\\H]]3]11 	&VLL!!!LLNNNHV]]__%%%	& 	& 	& 	& 	& 	& 	& 	& 	& 	& 	& 	& 	& 	& 	& 	
8Y'''	WS!122BK@@FF 	 	 	FFF	!           	   "!!! 	 	 	D		   "!!!!" 	 	 	D	t|34444   s   H" "AD>2H" >EH" EH" 1F H" F H" F  H" &G :H" G%%H" )(H 
HH"I$(II
IIII",J 
JJOptional[Dict[str, Any]]c                    |                      d          }t          |t                    sd S |                     |          }t          |t                    rt          |          nd S )NrO  )r   r   r0   )r   r   rO  r   s       r2   r   r     sZ    {++Ii&& tMM+&&E$UD11;4;;;t;r1   r   c                    |                      di           }t          |t                    si | d<   | d         }|||<   || d<   d S )NrO  rS  )rW  r   r0   )r   r   r   rO  s       r2   r   r     sX    %%k266Ii&& ,"$
;{+	"Ik$/J !!!r1   c                   t                      }|                    d          }t          |t                    si }| t          |          S |                    |           }t          |t                    rt	          |          ng S )z<Return the persisted credential pool, or one provider slice.rP  )r   r   r   r0   list)r   r   poolprovider_entriess       r2   read_credential_poolr}    s~    !##J>>+,,DdD!! Dzzxx,,%/0@$%G%GO4 !!!ROr1   entriesList[Dict[str, Any]]c                   t                      5  t                      }|                    d          }t          |t                    si }||d<   t          |          || <   t          |          cddd           S # 1 swxY w Y   dS )z7Persist one provider's credential pool under auth.json.rP  N)rJ  r   r   r   r0   rz  ru  )r   r~  r   r{  s       r2   write_credential_poolr    s    			 , ,%''
~~/00$%% 	1D,0J() MM[
++, , , , , , , , , , , , , , , , , ,s   A A<<B B r   c                   t                      5  t                      }|                    di           }|                    | g           }||vr|                    |           t	          |           ddd           dS # 1 swxY w Y   dS )z@Mark a credential source as suppressed so it won't be re-seeded.suppressed_sourcesN)rJ  r   rW  appendru  )r   r   r   
suppressedprovider_lists        r2   suppress_credential_sourcer    s    			 % %%''
**+?DD
"--k2>>&&  ((($$$% % % % % % % % % % % % % % % % % %s   A#A??BBc                    	 t                      }|                    di           }||                    | g           v S # t          $ r Y dS w xY w)z=Check if a credential source has been suppressed by the user.r  F)r   r   r   )r   r   r   r  s       r2   is_source_suppressedr    sa    %''
^^$8"==
R8888   uus   ;> 
AAc                >    t                      }t          ||           S )z4Return persisted auth state for a provider, or None.)r   r   )r   r   s     r2   get_provider_auth_stater    s    !##J
K888r1   c                 H    t                      } |                     d          S )z8Return the currently active provider ID from auth store.rS  )r   r   r   s    r2   get_active_providerr    s     !##J>>+,,,r1   c                   | pd                                                                 }	 t                      }|                    d          pd                                                                 }|r||k    rdS n# t          $ r Y nw xY w	 ddlm}  |            }|                    d          }t          |t                    rC|                    d          pd                                                                 }||k    rdS n# t          $ r Y nw xY wdh}t                              |          }	|	r?|	j
        d	k    r4|	j        D ],}
|
|v rt          t          j        |
d                    r dS -d
S )a  Return True only if the user has explicitly configured this provider.

    Checks:
      1. active_provider in auth.json matches
      2. model.provider in config.yaml matches
      3. Provider-specific env vars are set (e.g. ANTHROPIC_API_KEY)

    This is used to gate auto-discovery of external credentials (e.g.
    Claude Code's ~/.claude/.credentials.json) so they are never used
    without the user's explicit choice.  See PR #4210 for the same
    pattern applied to the setup wizard gate.
    r   rS  Tr   )load_configr   r   r[   r:   F)r   r   r   r   r   r   r  r   r0   r   r   r)   r   r   r   )r   
normalizedr   activer  cfg	model_cfgcfg_provider_IMPLICIT_ENV_VARSr   r   s              r2   !is_provider_explicitly_configuredr    s    #**,,2244J%''
..!2339r@@BBHHJJ 	f
**4   	111111kmmGGG$$	i&& 	%MM*55;BBDDJJLLLz))t    44##J//G 7$	11/ 	 	G,,, 7B!7!788 tt 5s%   AA> >
B
BA;D 
DDc                $   t                      5  t                      }| p|                    d          }|s	 ddd           dS |                    di           }t          |t                    si }||d<   |                    d          }t          |t                    si }||d<   d}||v r||= d}||v r||= d}|s	 ddd           dS |                    d          |k    rd|d<   t          |           ddd           n# 1 swxY w Y   dS )z
    Clear auth state for a provider. Used by `hermes logout`.
    If provider_id is None, clears the active provider.
    Returns True if something was cleared.
    rS  NFrO  rP  T)rJ  r   r   r   r0   ru  )r   r   targetrO  r{  cleareds         r2   clear_provider_authr  G  s    
		 % %%''
A
/@ A A 		% % % % % % % % NN;33	)T** 	0I&/J{#~~/00$%% 	1D,0J()Y&!GT>>VG 	3% % % % % % % %4 >>+,,66,0J()$$$9% % % % % % % % % % % % % % %: 4s   )DA:D-DD	D	c                     t                      5  t                      } d| d<   t          |            ddd           dS # 1 swxY w Y   dS )z
    Clear active_provider in auth.json without deleting credentials.
    Used when the user switches to a non-OAuth provider (OpenRouter, custom)
    so auto-resolution doesn't keep picking the OAuth provider.
    NrS  )rJ  r   ru  r  s    r2   deactivate_providerr  m  s     
		 % %%''
(,
$%$$$% % % % % % % % % % % % % % % % % %s   #?AAprovider_namec                j   	 ddl m}  |            }|sdS dg}|D ]s}|j        dk    rdnd}|                    d| d	|j                    |j        r|j                                        d         nd}|r|                    d
|            td                    |          S # t          $ r Y dS w xY w)zReturn a helpful hint string when provider resolution fails.

    Checks for common config.yaml mistakes (malformed custom_providers, etc.)
    and returns a human-readable diagnostic, or empty string if nothing found.
    r   )validate_config_structurer   uC   Config issue detected — run 'hermes doctor' for full diagnostics:r   ERRORWARNINGz  [z] u       → r]  )	r   r  severityr  r   hint
splitlinesjoinr   )r  r  issueslinesciprefix
first_hints          r2   %_get_config_hint_for_unknown_providerr  ~  s   ??????**,, 	2VW 	6 	6B "w 6 6WWIFLL5v555566646GC++--a00J 64
44555yy   rrs   B$ BB$ $
B21B2)explicit_api_keyexplicit_base_url	requestedr  r  c               Z   | pd                                                                 }i ddddddddddd	dd
dddddddddddddddddddddi dddddddddd d!d d"d d#d d$d%d&d%d'd(d)d(d*d(d+d,d-d,d.d/d0d/i d/d/d1d1d2d1d3d1d4d5d6d5d7d5d8d9d:d9d;d<d=d<d>d<d?d<d@dAdBdAdCdDdEdDdDdFdFdFdFdGdFdFdFdFdH
}|                    ||          }|dIk    rdIS |dFk    rdFS |t          v r|S |dk    r6t	          |          }dJ| dK}|r	|dL| z  }n|dMz  }t          |dNO          |s|rdIS 	 t                      }|                    dP          }|r/|t          v r&t          |          }	|	                    dQ          r|S n2# t          $ r%}
t          
                    dR|
           Y dS}
~
ndS}
~
ww xY wt          t          j        dT                    s!t          t          j        dU                    rdIS t                                          D ]J\  }}|j        dVk    r|d k    r|j        D ]*}t          t          j        |dW                    r|c c S +K	 dXdYlm}  |            rd<S n# t&          $ r Y nw xY wt          dZd[O          )\a~  
    Determine which inference provider to use.

    Priority (when requested="auto" or None):
    1. active_provider in auth.json with valid credentials
    2. Explicit CLI api_key/base_url -> "openrouter"
    3. OPENAI_API_KEY or OPENROUTER_API_KEY env vars -> "openrouter"
    4. Provider-specific API keys (GLM, Kimi, MiniMax) -> that provider
    5. Fallback: "openrouter"
    autoglmrF   zz-aizz.aizhipugooglerB   zgoogle-geminizgoogle-ai-studiozx-airf   zx.aigrokkimirK   zkimi-for-codingmoonshotzkimi-cnrN   zmoonshot-cnzarcee-airP   arceeaizminimax-chinar_   
minimax_cnclauderW   zclaude-codegithubr9   zgithub-copilotzgithub-modelszgithub-modelzgithub-copilot-acpr?   zcopilot-acp-agent	aigatewayrj   vercelzvercel-ai-gatewayopencoderm   zenzqwen-portalr7   qwen-clir8   z
gemini-clizgemini-oauthhfrv   zhugging-facezhuggingface-hubmimory   zxiaomi-mimoawsr   zaws-bedrockzamazon-bedrockamazongorp   zopencode-go-subkilors   z	kilo-codecustomr|   )
zkilo-gatewaylmstudioz	lm-studio	lm_studioollamaollama_cloudvllmllamacppz	llama.cppz	llama-cpp
openrouterzUnknown provider 'z'.z

z` Check 'hermes model' for available providers, or run 'hermes doctor' to diagnose config issues.invalid_provider)r   rS  	logged_inz)Could not detect active auth provider: %sNOPENAI_API_KEYOPENROUTER_API_KEYr:   r   r   has_aws_credentialszNo inference provider configured. Run 'hermes model' to choose a provider and model, or set an API key (OPENROUTER_API_KEY, OPENAI_API_KEY, etc.) in ~/.hermes/.env.no_provider_configured)r   r   r   r   r  r   r   get_auth_statusr   r   r   r   r   r   itemsr   r)   agent.bedrock_adapterr  ImportError)r  r  r  r  _PROVIDER_ALIASES_config_hintmsgr   r  statusepidr   r   r  s                  r2   resolve_providerr    s     %v,,..4466Jue%+U4;U(+X7I8 	 u '-e 		  1-	 BL]	
 	#
 &34D 	G '  	 (4\ 	+  -k 	) .y 	 %3I 	m .A- 	\ $,\ <OP\ 	N %*> 	| &0   @L\ \o  qD FR  Tg iw  yL 	m ,] =N}  	!  (!" 	y#" (#" 5Ei#" RZ[d#$ 	m%$ /%& 	
'& (' & FP8(NhH1  4 #&&z:>>J\!!|Xx&&&V<ZHH1:111 	v(,(((CCuuC"45555  , |E%''
 122 	f 111$V,,Fzz+&&  E E E@!DDDDDDDDE #34455 9J29UiKjKj9k9k | *//11 
 
W	)) )/ 	 	G 7B!7!788 




	======   	9	    	3 &	   s+   	AF 
G(GG9J 
JJOptional[float]c                l   t          | t                    r| sd S |                                 }|sd S |                    d          r|d d         dz   }	 t	          j        |          }n# t          $ r Y d S w xY w|j         |                    t          j
                  }|                                S )NZ+00:00)tzinfo)r   r   r   endswithr   fromisoformatr   r  rk  r   ra  	timestamp)r   textparseds      r2   _parse_iso_timestampr  
  s    eS!!  t;;==D t}}S $CRCy8#'--   tt}x|44s   A* *
A87A8expires_at_isoskew_secondsc                \    t          |           }|dS |t          j                    |z   k    S )NT)r  r6  )r  r  expires_epochs      r2   _is_expiringr    s0    (88MtTY[[<788r1   
expires_inc                h    	 t          |           }n# t          $ r d}Y nw xY wt          d|          S )Nr   )r   r   r7  )r  ttls     r2   _coerce_ttl_secondsr  "  sF    *oo   q#;;s    !!c                    t          | t                    sd S |                                                     d          }|r|nd S )N/)r   r   r   rstrip)r   r   s     r2   _optional_base_urlr  *  sC    eS!! tkkmm""3''G'774'r1   c                   t          | t                    r|                     d          dk    ri S |                     d          d         }|ddt	          |          dz  z
  dz  z  z  }	 t          j        |                    d                    }t          j	        |
                    d                    }n# t          $ r i cY S w xY wt          |t                    r|ni S )Nr^  rZ  r   =r   r  )r   r   countsplitr   base64urlsafe_b64decoder   r   rU  decoder   r0   )r   r  r  claimss       r2   _decode_jwt_claimsr  1  s    eS!! U[[%5%5%:%:	kk#q!Gsq3w<<!++q011G&w~~g'>'>??CJJw//00   			--56625s   +AB: :C	C	access_tokenc                
   t          |           }|                    d          }t          |t          t          f          sdS t	          |          t          j                    t          dt          |                    z   k    S )NexpFr   )r  r   r   r   r   r6  r7  )r  r  r  r  s       r2   _codex_access_token_is_expiringr  >  si    --F
**U

CcC<(( u::$)++As</@/@(A(AABBr1   c                 4    t          j                    dz  dz  S )Nz.qwenzoauth_creds.json)r	   homer'   r1   r2   _qwen_cli_auth_pathr	  F  s    9;; #555r1   c                 n   t                      } |                                 st          ddd          	 t          j        |                     d                    }n+# t          $ r}t          d|  d| dd	          |d }~ww xY wt          |t                    st          d
|  ddd          |S )NzAQwen CLI credentials not found. Run 'qwen auth qwen-oauth' first.r7   qwen_auth_missingr   r   r  r)  z)Failed to read Qwen CLI credentials from : qwen_auth_read_failedz Invalid Qwen CLI credentials in r^  qwen_auth_invalid)	r	  r1  r   r   rU  rV  r   r   r0   )	auth_pathdatar   s      r2   _read_qwen_cli_tokensr  J  s   #%%I 
O!$
 
 
 	

z)--w-??@@   J	JJSJJ!(
 
 
 		 dD!! 
;y;;;!$
 
 
 	

 Ks   (A 
B)BBtokensc                d   t                      }|j                            dd           |                    d          }|                    t          j        | dd          dz   d           t          j        |t          j
        t          j        z             |                    |           |S )	NTr%  z.tmprZ  )r\  r  r]  r  r)  )r	  r-  r.  r   r4  r   r  r   ro  r2  rp  rq  rk  )r  r  rr  s      r2   _save_qwen_cli_tokensr  c  s    #%%I4$777$$V,,H
6!tDDDtKV]^^^HXt|dl2333Yr1   expiry_date_msc                    	 t          |           }n# t          $ r Y dS w xY wt          j                    t          dt          |                    z   dz  |k    S )NTr     )r   r   r6  r7  )r  r  	expiry_mss      r2   _qwen_access_token_is_expiringr  m  se    ''		   ttIKK#a\!2!2333t;yHHs    
        4@c                   t          |                     dd          pd                                          }|st          ddd          	 t	          j        t          ddd	d|t          d
|          }n(# t          $ r}t          d| dd          |d }~ww xY w|j	        dk    r5|j
                                        }t          d|rd| ndz   dd          	 |                                }n(# t          $ r}t          d| dd          |d }~ww xY wt          |t                    r7t          |                    dd          pd                                          st          ddd          |                    d          }	 t          |          }n# t          $ r d}Y nw xY wt          |                    dd          pd                                          t          |                    d|          p|                                          t          |                    d|                     dd                    pd                                          pdt          |                    d|                     dd                    pd                                          t          t          j                    dz            t!          d|          dz  z   d}	t#          |	           |	S )Nrefresh_tokenr   z@Qwen OAuth refresh token missing. Re-run 'qwen auth qwen-oauth'.r7   qwen_refresh_token_missingr  !application/x-www-form-urlencodedr   )r   Accept
grant_typer  r"   )r   r  r   zQwen OAuth refresh failed: qwen_refresh_failedi  z9Qwen OAuth refresh failed. Re-run 'qwen auth qwen-oauth'.z Response: z*Qwen OAuth refresh returned invalid JSON: qwen_refresh_invalid_jsonr  z1Qwen OAuth refresh response missing access_token.qwen_refresh_invalid_responser  i`T  
token_typeBearerresource_urlzportal.qwen.air  r   )r  r  r&  r(  expiry_date)r   r   r   r   r   r   QWEN_OAUTH_TOKEN_URLQWEN_OAUTH_CLIENT_IDr   r   r  r   r   r0   r   r6  r7  r  )
r  r"  r  responser   bodyr  r  expires_in_seconds	refresheds
             r2   _refresh_qwen_cli_tokensr0  u  s   

?B77=2>>DDFFM 
N!-
 
 
 	
:  C, 
 .!.1 
 $
 
 
    /#//!&
 
 
 		 s""}""$$G'+3#T###5!&	
 
 
 	
--//   >>>!,
 
 
 		 gt$$ 
CNB0O0O0USU,V,V,\,\,^,^ 
?!0
 
 
 	
 \**J) __ ) ) )() GKK;;ArBBHHJJW[[-HHYMZZ``bb'++lFJJ|X4V4VWWc[cddjjllxpxGKK

>Sc8d8deeyiyzz  A  A  C  C49;;-..Q8J1K1Kd1RR I )$$$sB   )A7 7
BBB C5 5
D?DDF! !F0/F0FT)force_refreshrefresh_if_expiringrefresh_skew_secondsr1  r2  r3  c           	        t                      }t          |                    dd          pd                                          }t	          |           }|s%|r#t          |                    d          |          }|rFt          |          }t          |                    dd          pd                                          }|st          ddd          t          j	        dd                                          
                    d	          pt          }d||d
|                    d          t          t                                dS )Nr  r   r)  z?Qwen OAuth access token missing. Re-run 'qwen auth qwen-oauth'.r7   qwen_access_token_missingr  HERMES_QWEN_BASE_URLr  r  )r   r   r:   r   expires_at_msrK  )r  r   r   r   r   r  r0  r   r   r   r  DEFAULT_QWEN_BASE_URLr	  )r1  r2  r3  r  r  should_refreshr   s          r2    resolve_qwen_runtime_credentialsr:    sP    #$$Fvzz."55;<<BBDDL-((N i1 i7

=8Q8QSghh I)&116::nb99?R@@FFHH 
M!,
 
 
 	
 y/44::<<CCCHHaLaH M22,..//  r1   c                 J   t                      } 	 t          d          }dt          |           |                    d          |                    d          |                    d          dS # t          $ r*}dt          |           t          |          dcY d }~S d }~ww xY w)	NF)r2  Tr   r:   r7  )r  rK  r   r:   r7  r  rK  r   )r	  r:  r   r   r   )r  credsr   s      r2   get_qwen_auth_statusr>    s    #%%I
0UKKKYii))yy++"YY77
 
 	
  
 
 
YXX
 
 	
 	
 	
 	
 	
 	

s   AA. .
B"8BB"B"r1  c           	        	 ddl m}m}m}m} n(# t
          $ r}t          d| dd          |d}~ww xY w	  ||           }n2# |$ r*}t          t          |          d|j                  |d}~ww xY w |            }t          }d||d	|r|j
        ndt           |                      |r|j        nd
pd
|r|j        nd
pd
dS )z2Resolve runtime OAuth creds for google-gemini-cli.r   )GoogleOAuthError_credentials_pathget_valid_access_tokenload_credentialsz&agent.google_oauth is not importable: r8   google_oauth_module_missingr  Nr?  google-oauthr   )r   r   r:   r   r7  rK  email
project_id)agent.google_oauthrA  rB  rC  rD  r  r   r   r   !DEFAULT_GEMINI_CLOUDCODE_BASE_URL
expires_msrG  rH  )	r1  rA  rB  rC  rD  r   r  r=  r   s	            r2   (resolve_gemini_oauth_runtime_credentialsrL    sy   
	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
    :S::(.
 
 
 		--MJJJ   HH(
 
 
 		 E0H' .3=%****,,--!&.%++B52+08u''b?R	 	 	s'    
4/4A A4
%A//A4c                     	 ddl m} m} n# t          $ r dddcY S w xY w |             } |            }||j        sdt          |          ddS d	t          |          d
|j        |j        |j        |j        dS )z>Return a status dict for `hermes auth list` / `hermes status`.r   )rB  rD  Fzagent.google_oauth unavailable)r  r   Nznot logged inr<  TrF  )r  rK  r   r:   r7  rG  rH  )	rI  rB  rD  r  r  r   rK  rG  rH  )rB  rD  r  r=  s       r2   get_gemini_oauth_auth_statusrN  #  s    OJJJJJJJJJ O O O"-MNNNNNO!!##IE}E.}Y$
 
 	
 ^^ %)&  s    c                 l    t          t          j        d          pt          j        d                    S )zGDetect if running in an SSH session where webbrowser.open() won't work.
SSH_CLIENTSSH_TTY)r   r   r   r'   r1   r2   _is_remote_sessionrR  A  s)    	,''?29Y+?+?@@@r1   _lockrT  c                   | r5t                      5  t                      }ddd           n# 1 swxY w Y   nt                      }t          |d          }|st          dddd          |                    d          }t          |t                    st          ddd	d          |                    d
          }|                    d          }t          |t                    r|                                st          dddd          t          |t                    r|                                st          dddd          ||                    d          dS )zRead Codex OAuth tokens from Hermes auth store (~/.hermes/auth.json).
    
    Returns dict with 'tokens' (access_token, refresh_token) and 'last_refresh'.
    Raises AuthError if no Codex tokens are stored.
    Nr5   z?No Codex credentials stored. Run `hermes auth` to authenticate.codex_auth_missingTr   r  zICodex auth state is missing tokens. Run `hermes auth` to re-authenticate.codex_auth_invalid_shaper  r  zICodex auth is missing access_token. Run `hermes auth` to re-authenticate.codex_auth_missing_access_tokenJCodex auth is missing refresh_token. Run `hermes auth` to re-authenticate. codex_auth_missing_refresh_tokenlast_refresh)r  r[  )	rJ  r   r   r   r   r   r0   r   r   )rT  r   r   r  r  r  s         r2   _read_codex_tokensr\  N  s     ( 	, 	,)++J	, 	, 	, 	, 	, 	, 	, 	, 	, 	, 	, 	, 	, 	, 	, &''
 ^<<E 
M#%!	
 
 
 	
 YYx  Ffd## 
W#+!	
 
 
 	
 ::n--LJJ//MlC(( 
0B0B0D0D 
W#2!	
 
 
 	
 mS)) 
1D1D1F1F 
X#3!	
 
 
 	
 		.11  s   ,00r[  r  r[  c               D   t          j        dd                                          }|s#t          t	          j                    dz            }t	          |                                          dz  }	 i }|                                r(t          j	        |
                    d                    }t          |t                    si }|                    d          }t          |t                    si }| |d<   ||d	<   ||d<   |||d<   |j                            dd           |                    t          j        |d          d           |                    d           d
S # t&          t(          f$ r'}t*                              d||           Y d
}~d
S d
}~ww xY w)a  Write refreshed tokens back to ~/.codex/auth.json.

    OpenAI OAuth refresh tokens are single-use and rotate on every refresh.
    When Hermes refreshes a token it consumes the old refresh_token; if we
    don't write the new pair back, the Codex CLI (or VS Code extension) will
    fail with ``refresh_token_reused`` on its next refresh attempt.

    This mirrors the Anthropic write-back to ~/.claude/.credentials.json
    via ``_write_claude_code_credentials()``.
    
CODEX_HOMEr   .codexr  r  r)  r  r  r  Nr[  Tr%  rZ  r[  i  z*Failed to write refreshed tokens to %s: %s)r   r   r   r   r	   r  
expanduseris_filer   rU  rV  r   r0   r   r-  r.  r4  r  ro  r@  rF  r   r   )r  r  r[  
codex_homer  existingtokens_dictr   s           r2   _write_codex_cli_tokensrf    s     <,,2244J 1x/00
Z  ++--;IS#% 	Iz)"5"5w"5"G"GHHH(D)) 	Hll8,,+t,, 	K&2N#'4O$(#'3H^$td;;;TZ;;;gNNNW S S SA9cRRRRRRRRRSs   2C3E' 'F8FFDict[str, str]c                ~   |Dt          j        t          j                                                                      dd          }t                      5  t                      }t          |d          pi }| |d<   ||d<   d|d<   t          |d|           t          |           ddd           dS # 1 swxY w Y   dS )	zCSave Codex OAuth tokens to Hermes auth store (~/.hermes/auth.json).Nr  r  r5   r  r[  chatgpt	auth_mode)r   r`  r   ra  rb  rk  rJ  r   r   r   ru  )r  r[  r   r   s       r2   _save_codex_tokensrk    s   |HL11;;==EEhPSTT			 % %%''
$Z@@FB h ,n&kZ???$$$% % % % % % % % % % % % % % % % % %s   AB22B69B6r"  c          	        ~ t          |t                    r|                                st          dddd          t	          j        t          dt          |                              }t	          j        |ddi	          5 }|	                    t          d
did|t          d          }ddd           n# 1 swxY w Y   |j        dk    r)d}d|j         d}d}	 |                                }	t          |	t                    r|	                    d          }
t          |
t                    r(|
                                r|
                                }|	                    d          p|	                    d          }t          |t                    r+|                                rd|                                 }n# t           $ r Y nw xY w|dv rd}|dk    rd}d}t          |d||          	 |                                }n&# t           $ r}t          dddd          |d}~ww xY w|                    d          }t          |t                    r|                                st          ddd d          |                                |                                t#          j        t&          j                                                                      d!d"          d#}|                    d          }t          |t                    r+|                                r|                                |d<   |S )$z>Refresh Codex OAuth tokens without mutating Hermes auth state.rY  r5   rZ  Tr         @r   r   )r   r   r   r  r  r!  )r   r  Nr   codex_refresh_failedz'Codex token refresh failed with status r^  Fr   error_descriptionr   zCodex token refresh failed: >   invalid_grantinvalid_tokeninvalid_requestrefresh_token_reusedzCodex 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 `hermes auth` to re-authenticate.z*Codex token refresh returned invalid JSON.codex_refresh_invalid_jsonr  z6Codex token refresh response was missing access_token."codex_refresh_missing_access_tokenr  r  )r  r  r[  )r   r   r   r   r   Timeoutr7  r   Clientr   CODEX_OAUTH_TOKEN_URLCODEX_OAUTH_CLIENT_IDr   r   r0   r   r   r   r`  r   ra  rb  rk  )r  r  r"  r   clientr,  r   r   r   errerr_codeerr_descrefresh_payloadr   refreshed_accessupdatednext_refreshs                    r2   refresh_codex_oauth_purer    s    	mS)) 
1D1D1F1F 
X#3!	
 
 
 	
 mCU?%;%;<<==G	g:L/M	N	N	N 	
RX;;!#%HI-!.2   
 
	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 s""%SH<PSSS 
	--//C#t$$ P777++h,, ,1A1A ,#>>++D77#677M3779;M;Mh,, P1A1A POX^^=M=MOOG 	 	 	D	HHH#)))=   $#-	
 
 
 	
"--//   8#-!	
 
 

 	 '**>::&,, 
4D4J4J4L4L 
D#5!	
 
 
 	
 )..00&,,.. X\22<<>>FFxQTUU G
 #&&77L,$$ 8););)=)= 8#/#5#5#7#7 Ns=   )B::B>B>!C%G 
GG;H 
H3H..H3c           	        t          t          |                     dd          pd          t          |                     dd          pd          |          }t          |           }|d         |d<   |d         |d<   t	          |           t          |d         |d         |                    d                     |S )zzRefresh Codex access token using the refresh token.
    
    Saves the new tokens to Hermes auth store automatically.
    r  r   r  rl  r[  r]  )r  r   r   r0   rk  rf  )r  r"  r/  updated_tokenss       r2   _refresh_codex_auth_tokensr    s     )FJJ~r**0b11FJJ++1r22'  I
 &\\N%.~%>N>"&/&@N?#~&&&.!/"]]>22   
 r1   c                    t          j        dd                                          } | s#t          t	          j                    dz            } t	          |                                           dz  }|                                sdS 	 t          j	        |
                                          }|                    d          }t          |t                    sdS |                    d          }|                    d          }|r|sdS t          |d	          rt                              d
|           dS t          |          S # t"          $ r Y dS w xY w)zTry to read tokens from ~/.codex/auth.json (Codex CLI shared file).
    
    Returns tokens dict if valid and not expired, None otherwise.
    Does NOT write to the shared file.
    r_  r   r`  r  Nr  r  r  r   u7   Codex CLI tokens at %s are expired — skipping import.)r   r   r   r   r	   r  ra  rb  r   rU  rV  r   r   r0   r  r   r   r   )rc  r  r  r  r  r  s         r2   _import_codex_cli_tokensr  -  sZ    <,,2244J 1x/00
Z  ++--;I t*Y002233X&&&$'' 	4zz.11

?33 	= 	4 +<;; 	LLI9   4F||   tts%   AE .E 
+E 7E 
EEc                \   	 t                      }n# t          $ r}|j        dk    r t                      }|ret                              d           t          d           t          d           t          d           t          |           t                      }n Y d}~nd}~ww xY wt          |d                   }t          |
                    dd	          pd	                                          }t          t          j        d
d                    }t          |           }	|	s|rt!          ||          }	|	rt#          t%          t          t&                    |dz                       5  t          d          }t          |d                   }t          |
                    dd	          pd	                                          }t          |           }	|	s|rt!          ||          }	|	rGt)          ||          }t          |
                    dd	          pd	                                          }ddd           n# 1 swxY w Y   t          j        dd	                                                              d          pt,          }
d|
|d|
                    d          ddS )z@Resolve runtime credentials from Hermes's own Codex token store.rV  z?Migrating Codex credentials from ~/.codex/ to Hermes auth storeu?   ⚠️  Migrating Codex credentials to Hermes's own auth store.z4   This avoids conflicts with Codex CLI and VS Code.z<   Run `hermes auth` to create a fully independent session.
Nr  r  r   $HERMES_CODEX_REFRESH_TIMEOUT_SECONDS20rn  rl  FrS  HERMES_CODEX_BASE_URLr  r5   zhermes-auth-storer[  ri  )r   r   r:   r   r[  rj  )r\  r   r   r  r   r   printrk  r0   r   r   r   r   r   r   r   r  rJ  r7  AUTH_LOCK_TIMEOUT_SECONDSr  r  DEFAULT_CODEX_BASE_URL)r1  r2  r3  r  orig_err
cli_tokensr  r  refresh_timeout_secondsr9  r   s              r2   !resolve_codex_runtime_credentialsr  O  s   !##    =000 .//
 	KKYZZZSTTTHIIIQRRRz***%''DD DDDD" $x.!!Fvzz."55;<<BBDDL#BI.TVZ$[$[\\-((N ] 3 ]8G[\\ Qc%8Q2R2RTknqTq.r.rsss 	Q 	Q%E222D$x.))Fvzz."==CDDJJLLL!-00N" e(; e!@Oc!d!d Q3F<STT"6::nb#A#A#GRHHNNPP	Q 	Q 	Q 	Q 	Q 	Q 	Q 	Q 	Q 	Q 	Q 	Q 	Q 	Q 	Q 		)2..4466==cBB 	"!  #%00  s(    
B'BB""B'1C	II
I
insecure	ca_bundle
auth_stater  Optional[bool]r  r  
bool | strc                6   t          |t                    r|                    d          ni }t          |t                    r|ni }| t          |           n"t          |                    dd                    }|p<|                    d          p't	          j        d          pt	          j        d          }|rdS |rZt          |          }t          j                            |          s*dd l	} |j
        d                              d	|           d
S |S d
S )Ntlsr  Fr  HERMES_CA_BUNDLESSL_CERT_FILEr   zhermes.authuJ   CA bundle path does not exist: %s — falling back to default certificatesT)r   r0   r   r   r   r   r   pathisfilelogging	getLoggerr   )r  r  r  	tls_stateeffective_insecureeffective_caca_pathr  s           r2   _resolve_verifyr    s5    *4J)E)EM
u%%%2I'	488@		bI #.X)--
E2233 
 	 	&==%%	&9'((	& 9_%%	   u 	l##w~~g&& 	NNNGm,,44\   44r1   r{  httpx.Clientr    r"   r#   c                   |                      | dd|i|rd|ini           }|                                 |                                g d}fd|D             }|r%t          dd                    |                     S )	zFPOST to the device code endpoint. Returns device_code, user_code, etc.z/api/oauth/device/coder"   r#   r  )device_code	user_codeverification_uriverification_uri_completer  intervalc                    g | ]}|v|	S r'   r'   ).0fr  s     r2   
<listcomp>z(_request_device_code.<locals>.<listcomp>  s    ;;;QQd]]q]]]r1   z%Device code response missing fields: z, )r   raise_for_statusr   r   r  )r{  r    r"   r#   r,  required_fieldsmissingr  s          @r2   _request_device_coder    s     {{222
#(0b
   H ==??D  O <;;;/;;;G WU7ASASUUVVVKr1   r  poll_intervalc                &   t          j                     t          d|          z   }t          dt          |t                              }t          j                     |k     r$|                     | dd||d          }|j        dk    r)|                                }	d|	vrt          d          |	S 	 |                                }
n1# t          $ r$ |	                                 t          d	          w xY w|
                    d
d          }|dk    rt          j        |           |dk    r)t          |dz   d          }t          j        |           |
                    d          pd}t          | d|           t          d          )zDPoll the token endpoint until the user approves or the code expires.r   /api/oauth/tokenz,urn:ietf:params:oauth:grant-type:device_code)r"  r"   r  r  r   r  z+Token response did not include access_tokenz1Token endpoint returned a non-JSON error responser   r   authorization_pending	slow_down   rp  zUnknown authentication errorr  z*Timed out waiting for device authorization)r6  r7  min%DEVICE_AUTH_POLL_INTERVAL_CAP_SECONDSr   r   r   r   r   r  RuntimeErrorr   rC  rB  )r{  r    r"   r  r  r  rI  current_intervalr,  r  error_payload
error_codedescriptions                r2   _poll_for_tokenr    s    y{{SJ///H1c-1VWWXX
)++
 
 ;;000L&*   
 
 3&&mmooGW,, !NOOON	T$MMOOMM 	T 	T 	T%%'''RSSS	T #&&w33
000J'((($$"#3a#7<<J'(((#''(;<<^@^j99K99:::
C
D
DDs   3C .C6c                   |                      | dd||d          }|j        dk    r-|                                }d|vrt          ddd	d
          |S 	 |                                }n%# t          $ r}t          ddd
          |d }~ww xY wt          |                    dd                    }t          |                    d          pd          }	|dv }
t          |	d||
          )Nr  r  )r"  r"   r  r  r   r  z%Refresh response missing access_tokenr3   rr  Tr   zRefresh token exchange failedr   r   r   rq  rp     rq  rr  )r   r   r   r   r   r   r   )r{  r    r"   r  r,  r  r  r   r   r  relogins              r2   _refresh_access_tokenr  	  sO    {{,,,)"*
 
   H s""--//((C%+/TXZ Z Z ZI  I I I7!'$@ @ @EH	II }  /::;;Dm''(;<<_@_``K88G
K&tg
V
V
VVs   A- -
B7B

Bmin_ttl_secondsc                   |                      | ddd| idt          dt          |                    i          }|j        dk    r,|                                }d|vrt          d	d
d          |S 	 |                                }n%# t          $ r}t          dd
d          |d}~ww xY wt          |                    dd                    }t          |                    d          pd          }	|dv }
t          |	d
||
          )z0Mint (or reuse) a short-lived inference API key.z/api/oauth/agent-keyr   r   r  r   )r   r   r   r:   zMint response missing api_keyr3   server_errorr  zAgent key mint request failedNr   rp  r  r   )	r   r7  r   r   r   r   r   r   r   )r{  r    r  r  r,  r  r  r   r   r  r  s              r2   _mint_agent_keyr  ,  sa    {{000 ":L":":;R_)=)=!>!>?   H s""--//G##;%+.B B B BG  G G G7!'n> > >CF	GG }  .99::Dm''(;<<_@_``K88G
K&tg
V
V
VVs   6B 
B-B((B-)r"  verifyr!   r  	List[str]c                x   t          j        |          }t          j        |ddi|          5 }|                    |                     d           ddd| i          }d	d	d	           n# 1 swxY w Y   |j        d
k    rd|j         }	 |                                }t          |                    d          p|                    d          p|          }n2# t          $ r%}	t          
                    d|	           Y d	}	~	nd	}	~	ww xY wt          |dd          |                                }
|
                    d          }t          |t                    sg S g }|D ]}t          |t                    s|                    d          }t          |t                    rT|                                r@|                                }d|                                v r|                    |           dd}|                    |           t          t                              |                    S )z6Fetch available model IDs from the Nous inference API.r   r   r   r   r  r  z/modelsr   r   )r   Nr   z#/models request failed with status rp  r   z'Could not parse error response JSON: %sr3   models_fetch_failedr  r  r   hermesmidr   r   r(   c                j    |                                  }d|v rd| fS d|v rd|vrd| fS d|v rd| fS d| fS )Nopusr   prosonnetr      rZ  )r   )r  lows     r2   _model_priorityz*fetch_nous_models.<locals>._model_priorityx  sX    iikkS==s8OC<<HC//s8Os??s8O3xr1   )key)r  r   r   r(   )r   rw  rx  r   r  r   r   r   r   r   r   r   r   rz  r0   r   r   r  sortfromkeys)r!   r:   r"  r  r   r{  r,  r  r|  r  r  r  	model_idsitemmodel_idr  r  s                    r2   fetch_nous_modelsr  M  s    mO,,G	g:L/MV\	]	]	] 
ag::!((--666$&9&9&9:  
 

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 s""RH<PRR	G--//Ccgg&9::]cggg>N>N]R]^^KK 	G 	G 	GLLBAFFFFFFFF	Gf;PQQQQmmooG;;vDdD!! 	I 	" 	"$%% 	88D>>h$$ 	")9)9 	"..""C399;;&&S!!!    NNN'''i(()))s*   3A--A14A1AC 
D&DDc                    |                      d          }t          |t                    r|                                sdS t	          |                      d          |           S )N	agent_keyFagent_key_expires_at)r   r   r   r   r  )r   r  r  s      r2   _agent_key_is_usabler    sY    
))K
 
 Cc3 syy{{ uEII&<==OOOOr1   )r"  r  r  r3  c                   t                      5  t                      }t          |d          }|st          ddd          t	          |                    d                    p.t          j        d          pt          j        d          pt          	                    d          }t          |                    d	          pt                    }t          |||
          }|                    d          }	|                    d          }
t          |	t                    r|	st          ddd          t          |                    d          |          s|	cddd           S t          |
t                    r|
st          ddd          t          j        | r| nd          }t          j        |ddi|          5 }t%          ||||
          }ddd           n# 1 swxY w Y   t'          j        t*          j                  }t/          |                    d                    }|d         |d<   |                    d          p|
|d<   |                    d          p|                    d          pd|d<   |                    d          p|                    d          |d<   |                                |d<   ||d<   t'          j        |                                |z   t*          j                                                  |d<   ||d<   ||d	<   |du t          |t                    r|ndd|d<   t7          |d|           t9          |           |d         cddd           S # 1 swxY w Y   dS )zKResolve a refresh-aware Nous Portal access token for managed tool gateways.r3   &Hermes is not logged into Nous Portal.Tr  r    HERMES_PORTAL_BASE_URLNOUS_PORTAL_BASE_URLr  r"   r  r  r  ,No access token found for Nous Portal login.
expires_atN2Session expired and no refresh token is available.r   r   r   r  r{  r    r"   r  r  r&  r'  r#   obtained_attzFr  r  r  )rJ  r   r   r   r  r   r   r   DEFAULT_NOUS_PORTAL_URLr  r   DEFAULT_NOUS_CLIENT_IDr  r   r  r   rw  rx  r  r   r`  r   ra  r  rb  fromtimestampr  r   ru  )r"  r  r  r3  r   r   r    r"   r  r  r  r   r{  r/  r`  
access_ttls                   r2   resolve_nous_access_tokenr    sm    
		 H% H%%''
$Z88 	8!%    uyy):;;<< 'y122'y/00' '
&++ 	 		+..H2HII	 (iTYZZZyy00		/22,,, 	L 	>!%    EIIl335IJJ 	 =H% H% H% H% H% H% H% H%@ --- 	] 	D!%    -? LMM\12
 
 
 
	 - /#+	  I
	 
	 
	 
	 
	 
	 
	 
	 
	 
	 
	 
	 
	 
	 
	 l8<(((|)D)DEE
 ). 9n!*!?!?!P=o'mmL99`UYY|=T=T`X`l"w//E599W3E3Eg"}}m(l&4MMOOj(|
 
 
 )++ 	l $3 &k%#-fc#:#:D
 
e 	Z777$$$^$QH% H% H% H% H% H% H% H% H% H% H% H% H% H% H% H% H% H%s>   E	M0%AM0?GM0G#	#M0&G#	'E<M00M47M4r'  r&  r#   r  r  r  r  min_key_ttl_secondsr"  r  r  r1  
force_mintr&  r  r  r  r  r  r  c               0   | ||pt           |pt                              d          |pt                              d          |pd|pt          |||	|
t          |          |dd}t          |||          }t          j        |r|nd          }t          j	        |ddi|	          5 }|s)t          |                    d
          t                    r{t          ||d         |d         |d                   }t          j        t           j                  }t%          |                    d                    }|d         |d<   |                    d          p|d         |d<   |                    d          p|                    d          pd|d<   |                    d          p|                    d          |d<   t'          |                    d                    }|r||d<   |                                |d<   ||d<   t          j        |                                |z   t           j                                                  |d
<   |s,t/          |t1          dt3          |                              st5          ||d         |d         |          }t          j        t           j                  }|                    d          |d<   |                    d          |d<   |                    d
          |d<   |                    d          |d<   t          |                    dd                    |d <   |                                |d!<   t'          |                    d                    }|r||d<   d"d"d"           n# 1 swxY w Y   |S )#z4Refresh Nous OAuth state without mutating auth.json.r  r'  r  )r  r  r"   r    r!   r&  r#   r  r  r  r  r  r  r   r   r   r  r  r    r"   r  r  r  r  r&  r#   r!   r  r  r   r{  r    r  r  r:   r  key_idagent_key_idr  agent_key_expires_inreusedFagent_key_reusedagent_key_obtained_atN)r  r  r  DEFAULT_NOUS_INFERENCE_URLDEFAULT_NOUS_SCOPEr   r  r   rw  rx  r  r   !ACCESS_TOKEN_REFRESH_SKEW_SECONDSr  r   r`  r   ra  r  r  rb  r  r  r  r7  r   r  )r  r  r"   r    r!   r&  r#   r  r  r  r  r  r"  r  r  r1  r  r   r  r   r{  r/  r`  r  refreshed_urlmint_payload
minted_urls                              r2   refresh_nous_oauth_purer	    s   , %&8"8+F/FNNsSS1O5OWWX[\\ ,H,,"  4X"
 
 E" h)PUVVVFmHOODIIG	g:L/MV\	]	]	] '9ag 	L<)@)@Bcdd 	- %&7 8,#O4	  I ,x|,,C,Y]]<-H-HIIJ$-n$=E.!%.]]?%C%C%]u_G]E/""+--"="="d<AXAX"d\dE,&]]733Iuyy7I7IE'N.y}}=Q/R/RSSM <.;*+#&==??E- ",E,"*"8*,# # #ikk ,  	91%REXAYAY9Z9Z[[ 	9* %&7 8">2 3	  L ,x|,,C!-!1!1)!<!<E+$0$4$4X$>$>E.!,8,<,<\,J,JE(),8,<,<\,J,JE()(,\-=-=h-N-N(O(OE$%-0]]__E)*+L,<,<=Q,R,RSSJ 9.8*+O'9 '9 '9 '9 '9 '9 '9 '9 '9 '9 '9 '9 '9 '9 '9R Ls   'KNNNr  r"  r1  r  c                  |                      d          pi }t          |                      dd          |                      dd          |                      dd          |                      dt                    |                      dt                    |                      d	d
          |                      dt                    |                      d          |                      d          |                      d          |                      d          |||                     d          |                     d          ||          S )zRRefresh Nous OAuth from a state dict. Thin wrapper around refresh_nous_oauth_pure.r  r  r   r  r"   r   r    r!   r&  r'  r#   r  r  r  r  r  r  r  )r   r	  r  r  r  )r   r  r"  r1  r  r  s         r2   refresh_nous_oauth_from_stater  5  s    ))E


 bC"		."%%		/2&&		+|,,		#%<==		&(BCC99\844ii!344IIm,,99\**))K(("YY'=>>/'$$''+&&##   r1   )r  r"  r  r  r  c                   t          dt          |                     } t          j                    j        dd         t                      5  t                      t          d          st          ddd          t          
                    d                    p.t          j        d	          pt          j        d
          pt                              d          }t          
                    d                    pt          j        d          pt                              d          }t!          
                    d          pt"                    }dMfd}t%          ||          }	t'          j        |r|nd          }
t+          dt-          |          | t/          
                    d                               t'          j        |
ddi|	          5 }
                    d          }
                    d          }t3          |t                     r|st          ddd          t5          
                    d          t6                    rt3          |t                     r|st          ddd          t+          d d!t/          |          "           t9          ||||#          }t;          j        t>          j                   }tC          |
                    d$                    }|}|d         d<   |
                    d          p|d<   |
                    d%          p
                    d%          pd&d%<   |
                    d'          p
                    d'          d'<   t          |
                    d                    }|r|}|"                                d(<   |d$<   t;          j#        |$                                |z   t>          j         )          "                                d<   d         }d         }t+          d*d!t/          |          t/          |          +            |d,           d-}d}|s%tK          |           rd}t+          d./           nz	 t+          d0t/          |          1           tM          |||| 2          }nE# t          $ r7}t+          d3|j'        4           
                    d          }|j'        d5v rt3          |t                     r|rt+          d d6t/          |          "           t9          ||||#          }t;          j        t>          j                   }tC          |
                    d$                    }|d         d<   |
                    d          p|d<   |
                    d%          p
                    d%          pd&d%<   |
                    d'          p
                    d'          d'<   t          |
                    d                    }|r|}|"                                d(<   |d$<   t;          j#        |$                                |z   t>          j         )          "                                d<   d         }d         }t+          d*d6t/          |          t/          |          +            |d7           tM          |||| 2          }n Y d}~nd}~ww xY w|t;          j        t>          j                   }|
                    d8          d9<   |
                    d:          d;<   |
                    d          d<<   |
                    d$          d=<   t-          |
                    d>d-                    d?<   |"                                d@<   t          |
                    d                    }|r|}t+          dAt-          |
                    d>d-                    B           |d<   |d<   |d<   |	d-u t3          |	t                     r|	nddCdD<   ddd           n# 1 swxY w Y    |dE           ddd           n# 1 swxY w Y   
                    d9          }t3          |t                     r|st          dFddGH          
                    d<          }tQ          |          }|1t          dIt          |tS          j)                    z
                      n!tC          
                    d=                    }d||
                    d;          |||rdJndKdLS )Na  
    Resolve Nous inference credentials for runtime use.

    Ensures access_token is valid (refreshes if needed) and a short-lived
    inference key is present with minimum TTL (mints/reuses as needed).
    Concurrent processes coordinate through the auth store file lock.

    Returns dict with: provider, base_url, api_key, key_id, expires_at,
    expires_in, source ("cache" or "portal").
    r   Nr  r3   r  Tr  r    r  r  r  r!   NOUS_INFERENCE_BASE_URLr"   reasonr   r   r   c                d   	 t          d           t                     n8# t          $ r+}t          d| t	          |          j                    d }~ww xY wt          d| t                              d                    t                              d                               d S )Nr3   nous_state_persist_failed)r  r  
error_typenous_state_persistedr  r  )r  r  refresh_token_fpaccess_token_fp)r   ru  r   r  typer+   r  r   )r  r   r   r  r   s     r2   _persist_statez8resolve_nous_runtime_credentials.<locals>._persist_state~  s    
$Z??? ,,,,   / +!#Cyy1	     &'!3EIIo4N4N!O!O 2599^3L3L M M     s    $ 
A&AAr  r   nous_runtime_credentials_startr  )r  r  r  r  r   r   r  r  r  r  r  refresh_startaccess_expiring)r  r  r  r  r  r&  r'  r#   r  r  refresh_success)r  r  previous_refresh_token_fpnew_refresh_token_fppost_refresh_access_expiringFagent_key_reuser  
mint_start)r  r  r  
mint_error)r  r   r  mint_retry_after_invalid_tokenpost_refresh_mint_retryr:   r  r  r  r  r  r   r  r  mint_success)r  r   r  r  &resolve_nous_runtime_credentials_finalz*Failed to resolve a Nous inference API keyr  r  r   cacheportal)r   r   r:   r  r  r  r   )r  r   r   r   )*r7  r   re  rf  rg  rJ  r   r   r   r  r   r   r   r  r  r  r   r  r  r   rw  r  r   r  rx  r   r  r  r  r   r`  r   ra  r  rb  r  r  r  r  r   r  r6  )r  r"  r  r  r  r    r!   r"   r  r  r   r{  r  r  r/  r`  r  previous_refresh_tokenr  used_cached_keyr  r   latest_refresh_tokenr  r:   r  r  r  r   r  r   s                               @@@r2    resolve_nous_runtime_credentialsr+  T  s   $ b#&9":":;;*,,"3B3'K			 JA JA%''
$Z88 	DD%+dD D D D uyy):;;<< 'y122'y/00' '
&++ 	 uyy)=>>?? *y233*)
&++	 	
 		+..H2HII		 	 	 	 	 	 	 	( !(iTYZZZ-? LMM,#J'' 3/		/0J0JKK	
 	
 	
 	
 \'H>P3QZ`aaa U	ek 99^44L!IIo66MlC00 H H N)/$H H H H EIIl335VWW (?!-55 L] L#$X-3dL L L L # +,%7%F%F	    2!?'}  	 l8<000|1L1LMM
)6&(1.(An%)2)G)G)X=o&&/mmL&A&A&hUYY|E\E\&h`hl#!*w!7!7!M599W;M;Mg 29==AU3V3V W W  7)6&'*}}m$&0l#&.&<MMOOj0X\' ' ')++ l#  %^4 %o 6% +,.@AW.X.X);M)J)J    =>>> $O59L D"6u>Q"R"R D"&.KHHHHH@ $$/(:<(H(H   
 $3%%1CV$ $ $LL ! 6 6 6 $$/ X    ,199_+E+E($FFF&';SAA G0 G %+(3#C-?@T-U-U	    %:#)?&/?S% % %	 'l8<88%8|9T9T%U%U
09.0In-1:1O1O1gSgo..7mmL.I.I.pUYYWcMdMd.phpl+)2w)?)?)U599WCUCUg(:9==I];^;^(_(_( ?1>./2}}m,.8l+.6.DMMOOj8X\/ / /#)++ l+ (-^'<(-o(>$-(3#C6HI]6^6^1CM1R1R    ''@AAA'6#)?)5GZ( ( (
  %c6p 'l8<00%1%5%5i%@%@k"(4(8(8(B(Bn%0<0@0@0N0N,-0<0@0@0N0N,-,01A1A(E1R1R,S,S()14-./0@0@AU0V0VWW
 4)3&" + 0 05 A ABB    (7E#$*<E&'!*E+"eO'1&#'>'>HVVD E%LeU	 U	 U	 U	 U	 U	 U	 U	 U	 U	 U	 U	 U	 U	 U	n 	?@@@UJA JA JA JA JA JA JA JA JA JA JA JA JA JA JAX ii$$Ggs## >7 >D!'n> > > 	> 122J(44M $ 	As=49;;.//000 +A!B!BCC  &))N++  ,:''(  sd   Fa6+Ja2R<:a<[>H-[94a9[>>E
aa6a	a6a	a66a:=a:c                 N   	 ddl m}   | d          }|r|                                r|                                }|t	          |dd          pt	          |dd          }|rdt	          |d	d          pt	          |d
d          t	          |dd          pt	          |d
d          |t	          |dd          t	          |dd          t          t	          |dd                    dS n# t          $ r Y nw xY wt          d          }|s	dddddddS t          |                    d                    |                    d	          |                    d          |                    d          |                    d          t          |                    d                    dS )zStatus snapshot for `hermes status` output.

    Checks the credential pool first (where the dashboard device-code flow
    and ``hermes auth`` store credentials), then falls back to the legacy
    auth-store provider state.
    r   	load_poolr3   Nr  runtime_api_keyr   Tr    r   r!   r  r  r  )r  r    r!   r  access_expires_atr  has_refresh_tokenF)r  r    r!   r0  r  r1  )	agent.credential_poolr.  has_credentialsselectr+  r   r   r  r   )r.  r{  entryr  r   s        r2   get_nous_auth_statusr6  Q	  s   333333y   	D((** 	KKMME E>488 =u&7<<    %)+25:KT+R+R ,@&uj$??.5e=QSW.X.X /@&uj$??(4-4UL$-O-O07?UW[0\0\-1'%RV2W2W-X-X
 
 
     $F++E 
#"&!%$(!&
 
 	
 %))N3344 99%677#ii(<=="YY|44 %		*@ A A!%))O"<"<==  s   C$C( (
C54C5c            
        	 ddl m}   | d          }|r|                                r|                                }|wt	          |dd          pt	          |dd          }|rSt          |d          sCdt          t                                t	          |d	d          d
dt	          |dd           |dS n# t          $ r Y nw xY w	 t                      }dt          t                                |
                    d	          |
                    d          |
                    d          |
                    d          dS # t          $ r6}dt          t                                t          |          dcY d}~S d}~ww xY w)zStatus snapshot for Codex auth.
    
    Checks the credential pool first (where `hermes auth` stores credentials),
    then falls back to the legacy provider state.
    r   r-  r5   Nr/  r  r   Tr[  ri  zpool:r   unknown)r  r   r[  rj  r   r:   rj  r   r:   F)r  r   r   )r2  r.  r3  r4  r+  r  r   r  r   r  r   r   )r.  r{  r5  r:   r=  r   s         r2   get_codex_auth_statusr9  	  s   333333y(( 	D((** 	KKMME E#4d;; :unb99   #B7A#N#N %)&)/*;*;&<&<(/~t(L(L%."N'%)*L*L"N"N#*      
133o//00!IIn55;//ii))yy++
 
 	
  
 
 
o//00XX
 
 	
 	
 	
 	
 	
 	

s1   B3B7 7
CCA;E 
F+E?9F?Fc                   t                               |           }|r|j        dk    rddiS d}d}t          | |          \  }}d}|j        r,t          j        |j        d                                          }| dv rt          ||j	        |          }n|r|}n|j	        }t          |          | |j        ||t          |          dS )z<Status snapshot for API-key providers (z.ai, Kimi, MiniMax).r:   
configuredFr   rK   rN   )r;  r   r   
key_sourcer   r  )r   r   r   r   r*   r   r   r   r   r!   r   r   r   r   r:   r=  env_urlr   s         r2   get_api_key_provider_statusr@  	  s    ##K00G %g'944e$$GJ:;PPGZG B)G4b99??AA777)'73MwWW	 .- 7mm ']]  r1   c                   t                               |           }|r|j        dk    rddiS t          j        dd                                          p(t          j        dd                                          pd}t          j        dd                                          }|rt          j        |          nd	d
g}|j        r,t          j        |j        d                                          nd}|s|j	        }|rt          j        |          nd}t          |p|                    d                    | |j        ||||t          |p|                    d                    dS )z:Status snapshot for providers that run a local subprocess.r@   r;  FHERMES_COPILOT_ACP_COMMANDr   COPILOT_CLI_PATHr9   HERMES_COPILOT_ACP_ARGS--acp--stdioN
acp+tcp://)r;  r   r   commandargsresolved_commandr   r  )r   r   r   r   r   r   shlexr  r*   r!   shutilwhichr   r   r   )r   r   rH  raw_argsrI  r   rJ  s          r2   $get_external_process_provider_statusrO  	  sy   ##K00G %g'+===e$$ 		.3399;; 	9',,2244	 
 y2B77==??H$,F5;x   7I2FDBIBZbry1266<<>>>`bH .-07Av|G,,,T+Px/B/B</P/PQQ,*Oh.A.A,.O.OPP	 	 	r1   c                   | pt                      }|dk    rt                      S |dk    rt                      S |dk    rt                      S |dk    rt	                      S |dk    rt          |          S t                              |          }|r|j        dk    rt          |          S |r5|j        dk    r*	 dd	l
m}  |            |d
S # t          $ r	 d|ddcY S w xY wddiS )zGeneric auth status dispatcher.r3   r5   r7   r8   r?   r:   r   r   r  )r  r   Fzboto3 not installed)r  r   r   r  )r  r6  r9  r>  rN  rO  r   r   r   r@  r  r  r  )r   r  r   r  s       r2   r  r  	  sE   1/11F#%%%$&&&#%%%$$$+---3F;;;##F++G 37$	11*6222 \7$	11	\AAAAAA!4!4!6!6FKKK 	\ 	\ 	\!&FEZ[[[[[	\s   :C C C c                   t                               |           }|r|j        dk    rt          d|  d| d          d}d}t	          | |          \  }}d}|j        r,t          j        |j        d                                          }| dv rt          ||j
        |          }n<| dk    rt          ||j
        |          }n|r|                    d	          }n|j
        }| ||                    d	          |pd
dS )zwResolve API key and base URL for an API-key provider.

    Returns dict with: provider, api_key, base_url, source.
    r:   
Provider 'z' is not an API-key provider.r  r  r   r<  rF   r  default)r   r:   r   r   )r   r   r   r   r   r*   r   r   r   r   r!   r   r  r>  s         r2   $resolve_api_key_provider_credentialsrT  
  s<   
  ##K00G 
g'944CCCC #
 
 
 	
 GJ:;PPGZG B)G4b99??AA777)'73MwWW			('2LgVV	 .>>#&&-  OOC(()		  r1   c                   t                               |           }|r|j        dk    rt          d|  d| d          |j        r,t          j        |j        d                                          nd}|s|j        }t          j        dd                                          p(t          j        dd                                          pd	}t          j        d
d                                          }|rt          j
        |          nddg}|rt          j        |          nd}|s+|                    d          st          d| d| d          | d|                    d          |p||ddS )z>Resolve runtime details for local subprocess-backed providers.r@   rR  z&' is not an external-process provider.r  r  r   rB  rC  r9   rD  rE  rF  NrG  z(Could not find the Copilot CLI command 'zQ'. Install GitHub Copilot CLI or set HERMES_COPILOT_ACP_COMMAND/COPILOT_CLI_PATH.missing_copilot_clir?   r  process)r   r:   r   rH  rI  r   )r   r   r   r   r*   r   r   r   r!   rK  r  rL  rM  r   r  )r   r   r   rH  rN  rI  rJ  s          r2   -resolve_external_process_provider_credentialsrX  7
  s   ##K00G 
g'+===LLLL #
 
 
 	
 CJBZbry1266<<>>>`bH .- 		.3399;; 	9',,2244	 
 y2B77==??H$,F5;x   7I2FD07Av|G,,,T 
H$7$7$E$E 
]w ] ] ] &	
 
 
 	
   OOC((#.w  r1   default_modelc                4   t                      5  t                      }| |d<   t          |           ddd           n# 1 swxY w Y   t                      }|j                            dd           t                      }|                    d          }t          |t                    rt          |          }nBt          |t                    r+|                                rd|                                i}ni }| |d<   |r-|                                r|                    d          |d	<   n|                    d	d           |r!|                    dd
          }|rd|v r||d<   ||d<   |                    t          j        |d                     |S )a  Update config.yaml and auth.json to reflect the active provider.

    When *default_model* is provided the function also writes it as the
    ``model.default`` value.  This prevents a race condition where the
    gateway (which re-reads config per-message) picks up the new provider
    before the caller has finished model selection, resulting in a
    mismatched model/provider (e.g. ``anthropic/claude-opus-4.6`` sent to
    MiniMax's API).
    rS  NTr%  r   rS  r   r  r   r   Fr  )rJ  r   ru  r   r-  r.  r   r   r   r0   r   r   r  popr4  yaml	safe_dump)	r   r!   rY  r   config_pathconfigcurrent_modelr  cur_defaults	            r2   _update_config_for_providerrc  c
  s    
		 % %%''
(3
$%$$$% % % % % % % % % % % % % % % "##KTD999FJJw''M-&& ''			M3	'	' M,?,?,A,A  3 3 5 56			'Ij (06688 ( 2 9 9# > >	* 	j$'''
  1mmIr22 	1c[00#0Ii F7O4>&EBBBCCCs   #>AAc                 >   t                      } |                                 s| S t                      }|s| S |                    d          }t	          |t
                    rd|d<   d|v r
t          |d<   |                     t          j	        |d                     | S )z5Reset config.yaml provider back to auto after logout.r   r  r   r   Fr[  )
r   r1  r   r   r   r0   r   r4  r]  r^  )r_  r`  r   s      r2   _reset_config_providerre  
  s    !##K F JJwE% 4"j 3E*4>&EBBBCCCr1   r   r  ra  pricing#Optional[Dict[str, Dict[str, str]]]unavailable_modelsOptional[List[str]]
portal_urlc           
     F  !"#$%&' ddl m} |pg }g }r| v r|                               | D ]}||vr|                    |           t          |          t          |          z   }	t	          ot          fd|	D                                 %%rt          d |	D             d          dz   nd&i "d'd#d$%r|	D ]}                    |          }
|
rh ||
                    d	d
                    } ||
                    dd
                    }|
                    dd
          }|r ||          nd
}|rd$nd\  }}}|||f"|<   t          't          |          t          |                    't          #t          |                    #؉$rt          #d          #"#$%&'fd!d}d}%r1d}d| d
d& ddd' ddd' }$r|ddd# z  }||dz   z  }d}d}	 ddl	m
} !fd|D             }|                    d           |                    d            |pt                              d!          }|rwt          |           t                       |D ]!}t          | d !|           |            "t                       t          | d"| d#|            t                       d$}n|} |||d%d&d'dd|(          }|                                }dd)lm}  |             |d*S t                       |t          |          k     r||         S |t          |          k    r't#          d+                                          }|r|nd*S d*S # t&          t(          t*          t,          j        f$ r Y nw xY wt          |           t          t1          t          |          dz                       }t3          |d,          D ]'\  }}t          d|d| d- !|                      (t          |          }t          d|d,z   d| d.           t          d|dz   d| d/           |rm|pt                              d!          }t                       t          d| d0| d1|            |D ](}t          dd
d| d|  !|           |            )t                       	 	 t#          d2|dz    d3                                          } | sd*S t5          |           }d,|cxk    r|k    rn n||d,z
           S ||d,z   k    r't#          d+                                          }|r|nd*S ||dz   k    rd*S t          d4|dz               n2# t6          $ r t          d5           Y nt8          t:          f$ r Y d*S w xY w)6a  Interactive model selection. Puts current_model first with a marker. Returns chosen model ID or None.

    If *pricing* is provided (``{model_id: {prompt, completion}}``), a compact
    price indicator is shown next to each model in aligned columns.

    If *unavailable_models* is provided, those models are shown grayed out
    and unselectable, with an upgrade link to *portal_url*.
    r   )_format_price_per_mtokc              3  B   K   | ]}                     |          V  d S r   r   )r  mrf  s     r2   	<genexpr>z*_prompt_model_selection.<locals>.<genexpr>
  s-      &J&J!w{{1~~&J&J&J&J&J&Jr1   c              3  4   K   | ]}t          |          V  d S r   )r   )r  ro  s     r2   rp  z*_prompt_model_selection.<locals>.<genexpr>
  s(      //qCFF//////r1   )rS  rZ  r  Fpromptr   
completioninput_cache_readTr   r   r      c                    
rC                     | d          \  }}}d|d d|d }	r|d|d z  }| d | }n| }| k    r|dz  }|S )Nru  r(  >  <u     ← currently in usern  )r  inpoutr&  
price_partbase_price_cache	cache_colra  	has_cachehas_pricingname_col	price_cols         r2   _labelz'_prompt_model_selection.<locals>._label
  s     	*..sLAAOCeCSC9CCCCCyCCCCJ 8757977777
3H333z33DDD-,,Dr1   zSelect default model:z     r]  rx  r(  Inry  OutCachez  /Mtokz[2mz[0m)TerminalMenuc                ,    g | ]}d  |           S )ry  r'   )r  r  r  s     r2   r  z+_prompt_model_selection.<locals>.<listcomp>
  s*    999#%s%%999r1   z  Enter custom model namez  Skip (keep current)r  u     ── Upgrade at u    for paid models ──zAvailable free models:z-> )fg_greenbold)r  )cursor_indexmenu_cursormenu_cursor_stylemenu_highlight_stylecycle_cursorclear_screentitle)flush_stdinNzEnter model name: r   z. z. Enter custom model namez. Skip (keep current)u=   ── Unavailable models (requires paid tier — upgrade at u   ) ──z
Choice [1-z] (default: skip): zPlease enter 1-zPlease enter a number)hermes_cli.modelsrl  r  rz  r   anyr7  r   r   simple_term_menur  r  r  r  showhermes_cli.curses_uir  inputr   r  NotImplementedErrorr@  
subprocessSubprocessErrorr   	enumerater   r   KeyboardInterruptEOFError)(r  ra  rf  rh  rj  rl  _unavailableorderedr  
all_modelspr{  r|  
cache_readr&  default_idx
menu_titlepadheader_DIM_RESETr  choices_upgrade_urleffective_titlemenuidxr  r  	num_widthinchoicer  r  r  r  r  r  r  s(    ``                              @@@@@@@r2   _prompt_model_selectionr  
  s    988888%+L G &)33}%%%    gNN3 gl!3!33J wJ3&J&J&J&Jz&J&J&J#J#JKKKCNUs//J///;;;a??TUH 57LIII * 	3 	3CC  A -,,QUU8R-@-@AA,,QUU<-D-DEEUU#5r::
>HP..z:::b % $I",S%!$c5 1LIs3xxS::IIs5zz22II 	*Iq))I           K )J ) UcU2UUUUUDU9UUUUUUUUU 	2171Y11111Ffy((
 DF/11111199999992333./// #=&=EEcJJ 
	)*GGG# ; ;99FF3KK999::::GGGT\\|\\TZ\\]]]GGG6OO(O|$2!.!	
 	
 	
 iikk444444;4W3<CLL  /006688F#-66-t,gz7QR    
*CGq())**IGQ'' 4 43212y2222VVC[[223333GA	
<q1u
<y
<
<
<
<
<===	
8q1u
8y
8
8
8
8
8999 G"=&=EEcJJt4ttVbttlrttuuu 	G 	GCErEIEEEE$EsEVEEFFFF	GGG	BABBBCCIIKKF tf++CC}}}}1}}}}}sQw''A344::<<!'1vvT1At+AE++,,,, 	+ 	+ 	+)*****!8, 	 	 	44	!sO   7DM, (M, 09M, ,"NN	*U0 5)U0 /U0 	U0 U0 0VVVr  c                    ddl m}m}  |            }t          |                    d          t
                    r| |d         d<   nd| i|d<    ||           dS )u   Save the selected model to config.yaml (single source of truth).

    The model is stored in config.yaml only — NOT in .env.  This avoids
    conflicts in multi-agent setups where env vars would stomp each other.
    r   )save_configr  r   rS  N)r   r  r  r   r   r0   )r  r  r  r`  s       r2   _save_model_choicer  ]  sz     ;:::::::[]]F&**W%%t,, 0%-w	""$h/wKr1   c                z    t          d           t          d           t          d           t          d          )z9Deprecated: use 'hermes model' or 'hermes setup' instead.z,The 'hermes login' command has been removed.z(Use 'hermes auth' to manage credentials,zF'hermes model' to select a provider, or 'hermes setup' for full setup.r   )r  
SystemExit)rI  s    r2   login_commandr  n  s;    	
8999	
4555	
RSSS
Q--r1   c                r   	 t                      }|                    dd          }t          |t                    r|rt	          |d          st          d           	 t          d                                                                          }n# t          t          f$ r d}Y nw xY w|dv r[t          d|                    d	t                              }t                       t          d
           t          d| d           dS nt          d           n# t          $ r Y nw xY wt                      }|rt          d           t          d           	 t          d                                                                          }n# t          t          f$ r d}Y nw xY w|dv rt          |           t!          j        dd                                                              d          pt          }t          d|          }t                       t          d           t          d           t          d| d           dS t                       t          d           t          d           t                       t'                      }	t          |	d         |	                    d                     t          d|	                    d	t                              }t                       t          d
           ddlm}
 t          d |
             d           t          d| d           dS ) zNOpenAI Codex login via device code flow. Tokens stored in ~/.hermes/auth.json.r:   r   r   z6Existing Codex credentials found in Hermes auth store.z!Use existing credentials? [Y/n]: y)r   r  r  r5   r   Login successful!  Config updated: z (model.provider=openai-codex)Nz?Existing Codex credentials are expired. Starting fresh login...z: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]: r  )r  r  r  r  z=Credentials imported. Note: if Codex CLI refreshes its token,z<Hermes will keep working independently with its own session.zSigning in to OpenAI Codex...uF   (Hermes creates its own session — won't affect Codex CLI or VS Code)r  r[  r   )display_hermes_home  Auth state: z
/auth.json)r  r   r   r   r  r  r  r   r   r  r  rc  r  r   r  rk  r   r   r  _codex_device_code_loginhermes_constantsr  )rI  r   rd  _resolved_keyreuser_  r  	do_importr   r=  _dhhs              r2   _login_openai_codexr  v  sg   466
 !Y33mS)) 	Um 	UDcdqsuDvDv 	UJKKKABBHHJJPPRR/0   (((9.(,,WacyJzJz{{)***V;VVVWWW ) STTT    *++J JKKK_```	cddjjllrrttII+, 	 	 	III	$$z***y!8"==CCEELLSQQkUkH5nhOOKGGGQRRRPQQQR{RRRSSSF 
GGG	
)***	
RSSS	GGG$&&E uX		.(A(ABBB-neii
Tj>k>kllK	GGG	
<<<<<<	
-4466
-
-
-...	
J{
J
J
JKKKKKsO   AD 3B D B'$D &B''A D 	D 
D'&D'3F F$#F$c            
     \	   ddl } d}t          }	 t          j        t          j        d                    5 }|                    | dd|idd	i
          }ddd           n# 1 swxY w Y   n'# t          $ r}t          d| dd          d}~ww xY w|j        dk    rt          d|j         ddd          |	                                }|
                    dd          }|
                    dd          }t          dt          |
                    dd                              }	|r|st          ddd          t          d           t          d           t          d| d           t          d           t          d| d            t          d!           d"}
|                                 }d}	 t          j        t          j        d                    5 }|                                 |z
  |
k     rz|                     |	           |                    | d#||d$dd	i
          }|j        dk    r|	                                }n%|j        d%v rzt          d&|j         ddd'          ddd           n# 1 swxY w Y   n,# t           $ r t          d(           t#          d)          w xY w|t          d*dd+          |
                    d,d          }|
                    d-d          }| d.}|r|st          d/dd0          	 t          j        t          j        d                    5 }|                    t$          d,||||d1dd2i3          }ddd           n# 1 swxY w Y   n'# t          $ r}t          d4| dd5          d}~ww xY w|j        dk    rt          d6|j         ddd7          |	                                }|
                    d8d          }|
                    d9d          }|st          d:dd;          t'          j        d<d                                                              d=          pt.          }||d>|t1          j        t4          j                                                                      d?d@          dAdBdCS )DzBRun the OpenAI device code login flow and return credentials dict.r   Nzhttps://auth.openai.comr   )r   z!/api/accounts/deviceauth/usercoder"   r   r   )r   r   zFailed to request device code: r5   device_code_request_failedr  r   z$Device code request returned status r^  device_code_request_errorr  r   device_auth_idr  r  5z-Device code response missing required fields.device_code_incompletez!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)i  z/api/accounts/deviceauth/token)r  r  )i  i  z$Device auth polling returned status device_code_poll_error
Login cancelled.   z!Login timed out after 15 minutes.device_code_timeoutauthorization_codecode_verifierz/deviceauth/callbackzADevice auth response missing authorization_code or code_verifier.device_code_incomplete_exchange)r"  r   redirect_urir"   r  r  )r  r   zToken exchange failed: token_exchange_failedzToken exchange returned status token_exchange_errorr  r  z.Token exchange did not return an access_token.token_exchange_no_access_tokenr  r  )r  r  r  r  ri  zdevice-code)r  r   r[  rj  r   )r6  rz  r   rx  rw  r   r   r   r   r   r   r7  r   r  	monotonicrC  r  r  ry  r   r   r   r  r  r   r`  r   ra  rb  rk  )_timeissuerr"   r{  r   r   device_datar  r  r  max_waitstart	code_resp	poll_respr  r  r  
token_respr  r  r  r   s                         r2   r  r    s   &F%I
\%-"5"5666 	&;;<<<!9-');<   D	 	 	 	 	 	 	 	 	 	 	 	 	 	 	  
 
 
3c33#*F
 
 
 	

 3F43CFFF#*E
 
 
 	

 ))++KR00I __%5r::N3{z3??@@AAM 
N 
;#*B
 
 
 	
 

.///	
/000	
8&
8
8
8999	
!"""	
.)
.
.
.///	
;<<< HOOEI\%-"5"5666 	&//##e+h66M***"KK===,:SS+-?@ (  	 (C// ) 0 0I*j88#Wy?TWWW!/6N   	 	 	 	 	 	 	 	 	 	 	 	 	 	 	&    "###oo /#*?
 
 
 	
 #';R@@MM/266M222L 
] 
O#*K
 
 
 	


\%-"5"5666 	&%"6.$0!*%2  ()LM % 
 
J	 	 	 	 	 	 	 	 	 	 	 	 	 	 	  
 
 
+c++#*A
 
 
 	

 $$Gj.DGGG#*@
 
 
 	

 __F::nb11LJJ33M 
<#*J
 
 
 	
 		)2..4466==cBB 	"!  )*
 
  X\22<<>>FFxQTUU	 	 	s   'A.  A"A. "A&&A. )A&*A. .
B8BB='J $BJ:J J

J J
J )J;'M? &M3'M? 3M77M? :M7;M? ?
N#	NN#,  	r    r!   r"   r#   open_browserr"  r  r  r  r  c        	   
     N	   t           d         }	| p.t          j        d          pt          j        d          p|	j                            d          } |pt          j        d          p|	j                            d          }
|p|	j        }|p|	j        }t          j	        |          }|rdn|r|nd}t                      rd}t          d|	j         d	           t          d
|             |rt          d           n|rt          d| d           t          j        |ddi|          5 }t          || ||          }t          |d                   }t          |d                   }t!          |d                   }t!          |d                   }t                       t          d           t          d|            t          d|            |r5t#          j        |          }|rt          d           nt          d           t'          dt)          |t*                              }t          d| d           t-          || |t          |d                   ||          }d d d            n# 1 swxY w Y   t/          j        t2          j                  }t7          |                    dd!                    }|                                |z   }t=          |                    d"                    p|
}||
k    rt          d#|            i d$| d"|d%|d&|                    d&          p|d'|                    d'd(          d)|d)         d*|                    d*          d+|                                d,t/          j         |t2          j        -                                          d|d.|du tC          |t                    r|nd d/d0d d1d d2d d3d d4d d5d }	 tE          |||dd6          S # tF          $ r}|j$        d7k    r|                    d$tJ                                        d          }t                       t          d8           t          d9| d:           t                       t          d;           tM          d           d }~ww xY w)<zMRun the Nous device-code flow and return full OAuth state without persisting.r3   r  r  r  r  FTzStarting Hermes login via z...zPortal: z'TLS verification: disabled (--insecure)z$TLS verification: custom CA bundle ()r   r   r  )r{  r    r"   r#   r  r  r  r  zTo continue:z  1. Open: z  2. If prompted, enter code: z#  (Opened browser for verification)u=     Could not open browser automatically — use the URL above.r   z$Waiting for approval (polling every zs)...r  )r{  r    r"   r  r  r  Nr   r!   z%Using portal-provided inference URL: r    r"   r#   r&  r'  r  r  r  r  r  r  r  r  r  r  r  r  r  r
  r  z>Your Nous Portal account does not have an active subscription.z  Subscribe here: z/billingz<After subscribing, run `hermes model` again to finish setup.)'r   r   r   r    r  r!   r"   r#   r   rw  rR  r  r   rx  r  r   r   
webbrowserr5  r7  r  r  r  r   r`  r   ra  r  r   r  r  rb  r  r   r  r   r   r  r  )r    r!   r"   r#   r  r"  r  r  r  r   requested_inference_urlr   r  r{  r  verification_urlr  r  r  openedeffective_interval
token_datar`  token_expires_inr  resolved_inference_urlr  r   rj  s                                r2   _nous_device_code_loginr  H  s     'G 	#9-..	#9+,,	# "fSkk  	 	&9.//	&%fSkk	 
 .W.I"W]EmO,,G"*Ri1QTF 	
8w|
8
8
8999	
&_
&
&''' C78888	 CAYAAABBB	g:L/MV\	]	]	] #
ag*+	
 
 
 {+FGHHK011	\233
{:.//n.,..///:y::;;; 	W_%566F W;<<<<UVVV C2W$X$XYYN5GNNNOOO$+K677!"
 
 

9#
 #
 #
 #
 #
 #
 #
 #
 #
 #
 #
 #
 #
 #
 #
J ,x|
$
$C*:>>,+J+JKK#33J:>>*>??@@ 	#"  !888N6LNNOOO?4 	Y 	((1E	
 	jnn\8<< 	
>2 	88 	s}} 	h,ZHLIIISSUU 	& 	%#-fc#:#:D
 
 	T  	!" 	#$ 	%& 	D'( 	 )J,, 3+
 
 
 	
    8...#!#: fSkk  GGGRSSS;z;;;<<<GGGPQQQQ--s,   ,D>I66I:=I:+O? ?
R$	BRR$c                \	   t          | dd          pd}t          t          | dd                    }t          | dd          p't          j        d          pt          j        d          }	 t	          t          | d	d          t          | d
d          t          | dd          p|j        t          | dd          p|j        t          | dd           |||d	  	        }|d         }t                      5  t                      }|	                    d          }ddd           n# 1 swxY w Y   t                      5  t                      }	t          |	d|           t          |	          }
ddd           n# 1 swxY w Y   t                       t          d           t          d|
            d}	 |	                    d          p|	                    d          }t          |t                    r|st          ddd          ddlm}m}m}m}m} |	                    dg           }t                       g }|r4 |d          } |||          } |            }|r |||d          \  }}|	                    dd          }|r4t          d t-          |           d!           t/          ||||"          }nP|r?|pt0                              d#          }t          d$           t          d%| d&           nt          d'           nj# t4          $ r]}t          |t                    rt7          |          nt          |          }t                       t          d(|            Y d}~nd}~ww xY w|st                      5  t                      }	|r||	d<   n|	                    dd           t          |	           ddd           n# 1 swxY w Y   t                       t          d)           t          d*           dS t;          d||+          }|r!t=          |           t          d,|            t          d-| d.           dS # t>          $ r t          d/           tA          d0          t4          $ r&}t          d1|            tA          d2          d}~ww xY w)3z&Nous Portal device authorization flow.r   Nr   r  Fr  r  r  rj  inference_urlr"   r#   
no_browserr  r  r!   rS  r3   r  r  r  r  z,No runtime API key available to fetch modelsrr  r  r   )_PROVIDER_MODELSget_pricing_for_providerfilter_nous_free_modelscheck_nous_free_tierpartition_nous_models_by_tierT)	free_tierr    r   zShowing u=    curated models — use "Enter custom model name" for others.)rf  rh  rj  r  z#No free models currently available.zUpgrade at z to access paid models.z,No curated models available for Nous Portal.z?Login succeeded, but could not fetch available models. Reason: z:No provider change. Nous credentials saved for future use.z4  Run `hermes model` again to switch to Nous Portal.)rY  zDefault model set to: r  z (model.provider=nous)r  r  zLogin failed: r   )!r+  r   r   r   r  r"   r#   rJ  r   r   r   ru  r  r   r   r   r  r  r  r  r  r  r   r  r  r  r   r  r\  rc  r  r  r  )rI  r   r"  r  r  r  r!   _prior_storeprior_active_providerr   saved_toselected_modelruntime_keyr  r  r  r  r  r  rh  rf  r  _portal_urlr   r   r_  s                              r2   _login_nousr    s}   dIt44<OGD*e4455Hk4(( 	&9'((	&9_%% t,#D,==&t_dCCdK66K':K$..?'-$T<???+ &

 

 


 ((<=  	H 	H+--L$0$4$45F$G$G!	H 	H 	H 	H 	H 	H 	H 	H 	H 	H 	H 	H 	H 	H 	H  	4 	4)++J VZ@@@'
33H	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4
 	!""")x))*** *	_$..55W9W9WKk3// { B#(                 ),,VR88IGGG') 226::33IwGG	0022	 4Q4Q!7d5 5 51I1 !nn%6;;G FpYpppqqq!8w'9&" " "
 $ F:#:BB3GG;<<<ADAAABBBBDEEE 	_ 	_ 	_0:3	0J0JX',,,PSTWPXPXGGGG]T[]]^^^^^^^^	_  	 "## - --//
( <4IJ011NN#4d;;; ,,,- - - - - - - - - - - - - - - GGGNOOOHIIIF1&n
 
 
  	=~...;>;;<<<F;FFFGGGGG   "###oo   $s$$%%%mms   ,BQ 4$D$Q $D((Q +D(,Q =/E8,Q 8E<<Q ?E< 4Q 5EL Q 
M6AM1,Q 1M66Q 	<OQ OQ O/Q 
AQ 1R+!R&&R+c                   t          | dd          }|r*|t          vr!t          d|            t          d          t	                      }|p|}|st          d           dS |t          v rt          |         j        n|}t          |          rWt                       t          d| d           t          j	        d          rt          d	           dS t          d
           dS t          d| d           dS )z Clear auth state for a provider.r   NzUnknown provider: r   z#No provider is currently logged in.zLogged out of r^  r  z)Hermes will use OpenRouter for inference.z9Run `hermes model` or configure an API key to use Hermes.zNo auth state found for )
r+  r   r  r  r  r   r  re  r   r   )rI  r   r  r  r  s        r2   logout_commandr  L  s-   $
D11K {*;;;0;00111mm ""F"FF 34446<@Q6Q6Q%f-22W]M6"" ;   /}///0009)** 	O=>>>>>MNNNNN9999:::::r1   )r   r   )r:   r   r   r   r   r   r   r   )r   r
   r   r   r   r   )r   r   r   r   r   r   )r   )r:   r   r   r   r   r   )r   r   r   r   )r   r
   r   r   )r   r   )r  r   r  r   r  r
   r   r   )r   r	   )r"  r   r   )rK  rL  r   r%   )r   r%   r   r	   )r   r%   r   r   r   rv  )r   r%   r   r   r   r%   r   r   )r   r   r   r%   )r   r   r~  r  r   r	   )r   r   r   r   r   r   )r   r   r   r   r   r   )r   r   r   rv  )r   r   )r   r   r   r   )r   r   r   r   )r   r   )r  r   r   r   )r  r   r  r   r  r   r   r   )r   r
   r   r  )r  r
   r  r   r   r   )r  r
   r   r   )r   r
   r   r   )r   r
   r   r%   )r  r
   r  r   r   r   )r   r%   )r  r%   r   r	   )r  r
   r  r   r   r   )r  )r  r%   r"  r   r   r%   )r1  r   r2  r   r3  r   r   r%   )r1  r   r   r%   )rT  r   r   r%   )r  r   r  r   r[  r   r   r   )r  rg  r[  r   r   r   )r  r   r  r   r"  r   r   r%   )r  rg  r"  r   r   rg  )r   r   )r  r  r  r   r  rv  r   r  )
r{  r  r    r   r"   r   r#   r   r   r%   )r{  r  r    r   r"   r   r  r   r  r   r  r   r   r%   )
r{  r  r    r   r"   r   r  r   r   r%   )
r{  r  r    r   r  r   r  r   r   r%   )
r!   r   r:   r   r"  r   r  r  r   r  )r   r%   r  r   r   r   )
r"  r   r  r  r  r   r3  r   r   r   )$r  r   r  r   r"   r   r    r   r!   r   r&  r   r#   r   r  r   r  r   r  r   r  r   r  r   r"  r   r  r  r  r   r1  r   r  r   r   r%   )r   r%   r  r   r"  r   r1  r   r  r   r   r%   )r  r   r"  r   r  r  r  r   r  r   r   r%   )r   r   r   r%   )r   r   r!   r   rY  r   r   r	   )r   NNr   )r  r  ra  r   rf  rg  rh  ri  rj  r   r   r   )r  r   r   r   )r   r   r   r   )r    r   r!   r   r"   r   r#   r   r  r   r"  r   r  r   r  r   r  r   r   r%   )r.   
__future__r   r   r  r   rL  rK  r2  r  r   r  	threadingr6  re  r  
contextlibr   dataclassesr   r   r   r   pathlibr	   typingr
   r   r   r   r   r]  r   r   r   r   r  r   r  r+   r   r/  r   r0  rT  r  r  r  r  r  !DEFAULT_AGENT_KEY_MIN_TTL_SECONDSr  r  r  r8  DEFAULT_GITHUB_MODELS_BASE_URLDEFAULT_COPILOT_ACP_BASE_URLDEFAULT_OLLAMA_CLOUD_BASE_URLrz  ry  'CODEX_ACCESS_TOKEN_REFRESH_SKEW_SECONDSr+  r*  &QWEN_ACCESS_TOKEN_REFRESH_SKEW_SECONDSrJ  .GEMINI_OAUTH_ACCESS_TOKEN_REFRESH_SKEW_SECONDSr   r   r/   r   r   r   r   r   r   r   r   r   r  r   r  r  r  r  r  r!  localr,  rJ  r   ru  r   r   r}  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r	  r  r  r  r0  r:  r>  rL  rN  rR  r\  rf  rk  r  r  r  r  r  r  r  r  r  r  r  r  r	  r  r+  r6  r9  r@  rO  r  rT  rX  rc  re  r  r  r  r  r  r  r  r  r'   r1   r2   <module>r     s|     # " " " " "   				                    % % % % % % ( ( ( ( ( ( ( ( ' ' ' ' ' ' ' '       , , , , , , , , , , , ,   O O O O O O O O O O 0 0 0 0 0 0		8	$	$LLLL   EEEMMMM   FFF     < H % / $+ !$' !() %@ 3 !@ .  7 6 = *- '9 A ), & %< !13 .         E0
NN%/5(   E0 NN"1	  E0  .."0	  !E0, $"<	  -E08 ~~9M/  9E0H >>!$7/  IE0V nnT=*  WE0f 
>>9G'  gE0v >>7*(  wE0F nn&7-  GE0T ^^8-)  UE0d ~~=-+  eE0t 6\  uE0B ~~(S/-  CE0R ..?0.  SE0b 8.,  cE0r 
>>0)'  sE0B !. <0.   #N720   ">
 ;1/   <.,   ">=&&   n:,*   #N8,*   ~L+  {E0 E0 E0  E E E EX   4 6 
 
 
 
    89 	 	 	 	 	 	   L 4gY(S	<wi7S;>d>d>df|}CEkEkEk  nC  D# # # # #L& & & &Z1 1 1 1 1 1 1 1"   4D D D D- - - -
 >B [ [ [ [ [ [+ + + +2 2 2 2 $IO%% .G 6 6 6 6 6r< < < < <:! ! ! !H< < < <0 0 0 0	P 	P 	P 	P 	P	, 	, 	, 	,% % % %   9 9 9 9- - - -0 0 0 0f# # # # #L	% 	% 	% 	%"   4  $l '+'+	l l l l l lf   "9 9 9 9   ( ( ( (
6 
6 
6 
6C C C C6 6 6 6   2    Ms I I I I IE E E E ET   $ F	     @
 
 
 
<  ' ' ' ' ' 'T   <A A A A )- . . . . . .j #'	(S (S (S (S (S (SV% % % % %$ "	W W W W W Wt   6   H   $ G	< < < < < <J  $#+/	           N   6.E .E .E .Ej W  W  W  WFW W W WJ "6* 6* 6* 6* 6* 6*rP P P P "## AP% P% P% P% P% P%t #!% $#*.@!##%R R R R R Rp  A!     B  A!##v v v v v vz4 4 4 4n.
 .
 .
 .
b   >   <         6# # # #L% % % %^ $(4 4 4 4 4n   * 37.2m m m m m`   "   >L >L >L >LBN N N Nf &*(,#!#%@ @ @ @ @ @F~ ~ ~ ~B; ; ; ; ; ;s$   B BB"B' 'B10B1