
    i                   $   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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mZ ddlmZ dd	lmZm Z m!Z!m"Z" dd
l#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. ddl/m0Z0m1Z1m2Z2  ej3        e4          Z5	 ddl6Z6n# e7$ r dZ6Y nw xY w	 ddl8Z8n# e7$ r dZ8Y nw xY wdZ9dZ:dZ;dZ<dZ=dZ>dZ?dZ@dZAdZBdZCdZDdZEdZFdZGdZHdZIdZJdZKd ZLd!ZMd"ZNd#ZOd$ZPd%ZQd&ZRdZSd'ZTd(ZUdZVd)ZWd*ZXd+ZYd,ZZd-Z[dZ\d.]                    d/          Z^d0d1iZ_d2e`d3<   d4ZadZbd5Zce G d6 d7                      Zdi d8 edd8d9d:e;e<e=e>;          d< edd<d=d>eB?          d@ edd@dAd>eK?          dB eddBdCd>ea?          dD eddDdEdFdGdHdIJ          dK eddKdLdFeLdMdNJ          dO eddOdPdQeMdRS          dT eddTdUdFdVdWdXJ          dY eddYdZdFd[d\d]J          d^ edd^d_dFd`dadbJ          dc eddcdddFdedfg          dh eddhdidFeOdjdkJ          dl eddldmdFdndodpJ          dq eddqdrdFdsdtduJ          dv eddvdwdFddxdyJ          dz eddzd{d|eFeHeCeDd}eGeId~          d eddddFdddJ          i d eddddFdddJ          d eddddFdddJ          d eddddFdddJ          d eddddFdddJ          d eddddFdddJ          d eddddFdddJ          d eddddFdddJ          d eddddFdddJ          d eddddFdddJ          d eddddFdddJ          d eddddFdddJ          d eddddFdddJ          d eddddFdddĬJ          d eddddFeNddȬJ          d edddddddάJ          d eddddFdddӬJ          Zede`d<   dd؄ZfdZgdd܄Zhh dݣZiddߜddZjddZkd}d[dgdfdddgdfddg ddfddg ddfgZldddZmddZn G d deo          Zpdd ZqddZrddZsdddd
ZtddZuddZv ejw                    Zxee:fdd            ZydddZzddZ{ddZ|ddZ}ddddZ~ddZddZddd Zd d#Zdd%Zdd&Zdd'Zdd(Zdd)Zdd*Zddd+Zdd,Zdd.Z	 dddd/dd3Zd	d5Zd
d8Zdd:Zdd;Zdd<Zdd>Zdd?Zdd@ZddBZeVfddDZdddFZdGdeVdHddLZddMZdGdNddOZddPZdddSZdddTZ	 	 dddVZ	 	 dddWZdddXZdddYZddd\Zdd^ZdddZddfZd diZdjdkd!dmZddnd"dsZdEdkd#duZdEdkd$dvZdGde\dHddwZddxZd%dzZdd{Zdd|Zdd}d&dZdd'dZdEdkd(dZd)dZd*dZdGdeSdHddZd+dZddddd,dZd-dZd.dZd/dZd0dZdddd1dZd2dZddde@dd3dZde>dddde?ddddGdGdd4dZe?ddGdGdd5dZdZddd6dZe?ddddGdd7dZÐddZĐddZŐddZƐddZǐd8dZȐd8dZɐdddZʐd8dZːd8dZ	 dd9dZ͐ddZΐddZϐddZАddZ	 	 	 	 d:d;dɄZҐd<d˄ZӐdd̄ZԐdGd͜d=dτZՐddЄZ֐d>d҄Zאd?dӄZؐd@d؄ZِdAdلZd}dddڜdBd݄ZddGdޜdCdZeJddDdZݐddZސdEdZdddddddGddd	dFdZdEdZddZdS (G  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)BaseHTTPRequestHandler
HTTPServer)Path)AnyDictListOptional)parse_qs	urlencodeurlparse)get_hermes_homeget_config_pathread_raw_config)OPENROUTER_BASE_URL)atomic_replaceatomic_yaml_writeis_truthy_value         .@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$78257093-7e40-4613-99e0-527b14b39113z!group_id profile model.completionz*urn:ietf:params:oauth:grant-type:user_codezhttps://api.minimax.iozhttps://api.minimaxi.comz https://api.minimax.io/anthropicz"https://api.minimaxi.com/anthropic<   zhttps://portal.qwen.ai/v1zhttps://api.githubcopilot.comzacp://copilotzhttps://ollama.com/v1z#https://api.stepfun.ai/step_plan/v1z$https://api.stepfun.com/step_plan/v1app_EMoamEEZ73f0CkXaXp7hrannz#https://auth.openai.com/oauth/token f0304373b74a44d2b584a3fb70ca9e56z(https://chat.qwen.ai/api/v1/oauth2/tokenzhttps://accounts.spotify.comzhttps://api.spotify.com/v1z'http://127.0.0.1:43827/spotify/callbackzFhttps://hermes-agent.nousresearch.com/docs/user-guide/features/spotifyz'https://developer.spotify.com/dashboard )
zuser-modify-playback-statezuser-read-playback-statezuser-read-currently-playingzuser-read-recently-playedzplaylist-read-privatezplaylist-read-collaborativezplaylist-modify-publiczplaylist-modify-privatezuser-library-readzuser-library-modifyspotifySpotifyDict[str, str]SERVICE_PROVIDER_NAMESzcloudcode-pa://googlezdummy-lm-api-keyc                      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/   r0   r   dictr3   r6   r7   r4       4/home/ubuntu/.hermes/hermes-agent/hermes_cli/auth.pyr'   r'      s         //GGGIIINNNO     IEOOOO!E$777E7777     r>   r'   nouszNous Portaloauth_device_code)r)   r*   r+   r-   r.   r/   r0   openai-codexzOpenAI Codexoauth_external)r)   r*   r+   r.   
qwen-oauthz
Qwen OAuthgoogle-gemini-clizGoogle Gemini (OAuth)lmstudioz	LM Studioapi_keyzhttp://127.0.0.1:1234/v1)
LM_API_KEYLM_BASE_URL)r)   r*   r+   r.   r6   r7   copilotzGitHub Copilot)COPILOT_GITHUB_TOKENGH_TOKENGITHUB_TOKENCOPILOT_API_BASE_URLcopilot-acpzGitHub Copilot ACPexternal_processCOPILOT_ACP_BASE_URL)r)   r*   r+   r.   r7   geminizGoogle AI Studioz0https://generativelanguage.googleapis.com/v1beta)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_CODING_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.   r6   stepfunzStepFun Step Plan)STEPFUN_API_KEYSTEPFUN_BASE_URLarceezArcee AIzhttps://api.arcee.ai/api/v1)ARCEEAI_API_KEYARCEE_BASE_URLgmiz	GMI Cloudzhttps://api.gmi-serving.com/v1)GMI_API_KEYGMI_BASE_URLminimaxMiniMax)MINIMAX_API_KEYMINIMAX_BASE_URLminimax-oauthu   MiniMax (OAuth · minimax.io)oauth_minimaxglobal)regioncn_portal_base_urlcn_inference_base_url)r)   r*   r+   r-   r.   r/   r0   r3   	anthropic	Anthropiczhttps://api.anthropic.com)ANTHROPIC_API_KEYANTHROPIC_TOKENCLAUDE_CODE_OAUTH_TOKENANTHROPIC_BASE_URLalibabazAlibaba Cloud (DashScope)z6https://dashscope-intl.aliyuncs.com/compatible-mode/v1)DASHSCOPE_API_KEYDASHSCOPE_BASE_URLalibaba-coding-planzAlibaba Cloud (Coding Plan)z-https://coding-intl.dashscope.aliyuncs.com/v1)ALIBABA_CODING_PLAN_API_KEYr{   ALIBABA_CODING_PLAN_BASE_URL
minimax-cnzMiniMax (China))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nvidiaz
NVIDIA NIMz#https://integrate.api.nvidia.com/v1)NVIDIA_API_KEYNVIDIA_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tencent-tokenhubzTencent TokenHubz#https://tokenhub.tencentmaas.com/v1)TOKENHUB_API_KEYTOKENHUB_BASE_URLollama-cloudzOllama Cloud)OLLAMA_API_KEYOLLAMA_BASE_URLbedrockzAWS Bedrockaws_sdkz/https://bedrock-runtime.us-east-1.amazonaws.comr4   BEDROCK_BASE_URLzazure-foundryzAzure Foundryr,   )AZURE_FOUNDRY_API_KEYAZURE_FOUNDRY_BASE_URLz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_valuert   r,   )hermes_cli.configr   r   r6   osgetenv)r   varvalues      r?   get_anthropic_keyr     sh     0///// ->  c""8biR&8&8 	LLL	2r>   zhttps://api.kimi.com/codingdefault_urlenv_overridec                N    |r|S | s|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)rG   r   r   s      r?   _resolve_kimi_base_urlr     sB       *%% "!!r>   >   *****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      r?   has_usable_secretr     sZ    eS!! ukkmmG
7||j  u}}444u4r>   provider_idpconfigtuple[str, str]c                   | dk    re	 ddl m}m}  |            \  }}|r ||          |fS n=# t          $ r%}t                              d|           Y d}~nd}~wt          $ r Y nw xY wdS ddlm} |j	        D ]6} ||          pd
                                }	t          |	          r|	|fc S 7	 dd	lm}
  |
|           }|r|                                ro|                                }|rYt!          |d
d          pt!          |dd          }t#          |          
                                }t          |          r|d|  fS n# t          $ r Y nw xY wdS )zDResolve an API-key provider's token and indicate where it came from.rJ   r   )resolve_copilot_tokenget_copilot_api_tokenz#Copilot token validation failed: %sN)r,   r,   r   r,   	load_poolaccess_tokenruntime_api_keyzcredential_pool:)hermes_cli.copilot_authr   r   
ValueErrorloggerwarning	Exceptionr   r   r6   r   r   agent.credential_poolr   has_credentialspeekgetattrr(   )r   r   r   r   tokensourceexcr   env_varvalr   poolentrykeys                 r?    _resolve_api_key_provider_secretr     s    i	\\\\\\\\1133ME6 <,,U33V;;< 	G 	G 	GNN@#FFFFFFFF 	 	 	D	v//////+    }W%%+2244S!! 	 <	 333333y%% 	AD((** 	AIIKKE Ae^R88aGEK\^`<a<a#hhnn&&$S)) A @; @ @@@    6s-   #- 
A'AA'&A'1BE 
EEz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   )	rG   r   ep_idr  probe_modelsr  r   respr   s	            r?   detect_zai_endpointr  4  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 | s|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.
    rV   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   r=   hashlibsha256encode	hexdigestr   r  r  _save_provider_stateinfo)rG   r   r   
auth_storestatecachedr  detecteds           r?   _resolve_zai_base_urlr#  Z  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r>   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        r?   r0  zAuthError.__init__  s9     	!!! 	 0r>   )
r*  r(   r'  r(   r(  r+  r)  r   r   r,  )r8   r9   r:   r;   r0  __classcell__)r2  s   @r?   r%  r%    sX        66 "!&1 1 1 1 1 1 1 1 1 1 1 1r>   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(  )r4  s    r?   format_auth_errorr9    s    eY'' 5zz A@@@@z,,,C	
 	

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

 z...8888u::r>   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     r?   _token_fingerprintr=    sd    eS!! tkkmmG t>'..1122<<>>ssCCr>   c                 |    t          j        dd                                                                          } | dv S )NHERMES_OAUTH_TRACEr,   >   1onyestrue)r   r   r   r   )raws    r?   _oauth_trace_enabledrE    s8    
)("
-
-
3
3
5
5
;
;
=
=C,,,r>   sequence_ideventrG  fieldsr,  c                   t                      sd S d| i}|r||d<   |                    |           t                              dt	          j        |dd                     d S )NrH  rG  zoauth_trace %sTF)	sort_keysensure_ascii)rE  updater   r  r  dumps)rH  rG  rI  payloads       r?   _oauth_tracerP    sp    !! &.G -!,NN6
KK $*WSX"Y"Y"YZZZZZr>   r   c                 F   t                      dz  } t          j                            d          rpt	          j                    dz  dz                      d          }	 |                     d          }n# t          $ r | }Y nw xY w||k    rt          d|  d          | S )N	auth.jsonPYTEST_CURRENT_TESTz.hermesF)strictz8Refusing to touch real user auth store during test run: zq. Set HERMES_HOME to a tmp_path in your test fixture, or run via scripts/run_tests.sh for hermetic CI-parity env.)	r   r   environr  r   homeresolver   RuntimeError)pathreal_home_authresolveds      r?   _auth_file_pathr\    s    {*D 
z~~+,, )++	1K?HHPUHVV	||5|11HH 	 	 	HHH	~%%G4 G G G  
 Ks   A6 6BBc                 D    t                                          d          S )Nz.lock)r\  with_suffixr4   r>   r?   _auth_lock_pathr_    s    ((111r>   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_okr!   r;  encodingzr+za+      ?z%Timed out waiting for auth store lockg?)r   _auth_lock_holderrb  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       r?   _auth_store_lockr    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]r2   c                z   | pt                      } |                                 s
t          i dS 	 t          j        |                                           }nz# t          $ rm}|                     d          }	 dd l} |j	        | |           n# t          $ r Y nw xY wt                              d| ||           t          i dcY d }~S d }~w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	providersz.json.corruptr   uY   auth: failed to parse %s (%s) — starting with empty store. Corrupt file preserved at %sr  credential_poolsystemsnous_portalr@   )r  r  active_provider)r\  rn  AUTH_STORE_VERSIONr  loads	read_textr   r^  shutilcopy2r   r   r   r=   r  
setdefault)r  rD  r   corrupt_pathr  r  r  s          r?   r  r  -  s   ._..I @-B???@j,,..// @ @ @ ,,_==	MMMFLL1111 	 	 	D	+sL	
 	
 	

 .B????????@ #t 377;''..cgg/00$77 	{B'''
 #t BCGGI,>,>!E!E Bi.	G## ' 6If-I-6#@66DB B 	B *;;;sA   &A 
C!C	7BC	
BC	B)C	C	Cr  c                8   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/          ||           	 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 )NTrc  r  
updated_at   )indent
z.tmp..wr;  rf  )"r\  rj  rk  r  r   nowr   utc	isoformatr  rN  	with_namer*   r   getpiduuiduuid4hexrr  writeflushfsyncrv  r   r(   O_RDONLYr}  closern  unlinkchmodro  S_IRUSRS_IWUSR)r  r  rO  tmp_pathhandledir_fds         r?   _save_auth_storer  U  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]]__%%%	& 	& 	& 	& 	& 	& 	& 	& 	& 	& 	& 	& 	& 	& 	& 	x+++	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
 5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 )Nr  )r  r   r=   )r  r   r  r   s       r?   r  r  y  sZ    {++Ii&& tMM+&&E$UD11;4;;;t;r>   r   c                    |                      di           }t          |t                    si | d<   | d         }|||<   || d<   d S Nr  r  r  r   r=   )r  r   r   r  s       r?   r  r    sX    %%k266Ii&& ,"$
;{+	"Ik$/J !!!r>   T
set_activer  c                   |                      di           }t          |t                    si | d<   | d         }|||<   |r|| d<   d S d S r  r  )r  r   r   r  r  s        r?   _store_provider_stater    sl     %%k266Ii&& ,"$
;{+	"Ik 4(3
$%%%4 4r>   c                v    | pd                                                                 }|t          v p|t          v S Nr,   )r   r   r   r%   r   
normalizeds     r?   is_known_auth_providerr    s;    #**,,2244J**Rj<R.RRr>   c                    | pd                                                                 }|t          v rt          |         j        S t                              ||           S r  )r   r   r   r*   r%   r  r  s     r?   get_auth_provider_display_namer    sT    #**,,2244J&&& ,11!%%j+>>>r>   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.r  )r  r  r   r=   list)r   r  r   provider_entriess       r?   read_credential_poolr    s~    !##J>>+,,DdD!! Dzzxx,,%/0@$%G%GO4 !!!ROr>   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.r  N)r  r  r  r   r=   r  r  )r   r  r  r   s       r?   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)r  r  r  appendr  r   r   r  
suppressedprovider_lists        r?   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       r?   is_source_suppressedr    sa    %''
^^$8"==
R8888   uus   ;> 
AAc                   t                      5  t                      }|                    d          }t          |t                    s	 ddd           dS |                    |           }t          |t
                    r||vr	 ddd           dS |                    |           |s|                    | d           |s|                    dd           t          |           	 ddd           dS # 1 swxY w Y   dS )zClear a suppression marker so the source will be re-seeded on the next load.

    Returns True if a marker was cleared, False if no marker existed.
    r  NFT)	r  r  r  r   r=   r  removepopr  r  s        r?   unsuppress_credential_sourcer    s   
 
		  %''
^^$899
*d++ 		       
 #{33-.. 	&2M2M        	V$$$ 	.NN;--- 	7NN/666$$$                 s   :C4/C4AC44C8;C8c                >    t                      }t          ||           S )z4Return persisted auth state for a provider, or None.)r  r  )r   r  s     r?   get_provider_auth_stater    s    !##J
K888r>   c                 H    t                      } |                     d          S )z8Return the currently active provider ID from auth store.r  )r  r  r  s    r?   get_active_providerr    s     !##J>>+,,,r>   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,   r  Tr   )load_configr   r'  rx   rG   F)r   r   r  r  r   r   r  r   r=   r   r+   r6   r   r   r   )r   r  r  activer  cfg	model_cfgcfg_provider_IMPLICIT_ENV_VARSr   r   s              r?   !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}|                    d          |k    rd|d<   d}|s	 ddd           dS 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.
    r  NFr  r  T)r  r  r  r   r=   r  )r   r  targetr  r   cleareds         r?   clear_provider_authr  (  s    
		 % %%''
A
/@ A A 		% % % % % % % % NN;33	)T** 	0I&/J{#~~/00$%% 	1D,0J()Y&!GT>>VG>>+,,66,0J()G 	;% % % % % % % %< 	$$$=% % % % % % % % % % % % % % %> 4s   )DB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.
    Nr  )r  r  r  r  s    r?   deactivate_providerr  P  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:r4  ERRORWARNINGz  [z] u       → r  )	r   r  severityr  r*  hint
splitlinesjoinr   )r  r  issueslinesciprefix
first_hints          r?   %_get_config_hint_for_unknown_providerr  a  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          	        | 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 d1d.d2d3d4d3d5d6d7d6d8d6d9d:d;d:d<d=d>d=d=d=d?d?d@d?dAd?dBdCdDdCdEdCi dFdGdHdGdIdJdKdJdLdJdMdJdNdOdPdOdQdOdRdOdSdTdUdTdVdWdXdWdYdWdZdZd[dZdZd\d]d\d\d\d\d^}|                    ||          }|d_k    rd_S |d\k    rd\S |t          v r|S |dk    r6t	          |          }d`| da}|r	|db| z  }n|dcz  }t          |dde          |s|rd_S 	 t                      }|                    df          }|r/|t          v r&t          |          }	|	                    dg          r|S n2# t          $ r%}
t          
                    dh|
           Y di}
~
ndi}
~
ww xY wt          t          j        dj                    s!t          t          j        dk                    rd_S t                                          D ]H\  }}|j        dlk    r|dmv r|j        D ]*}t          t          j        |dn                    r|c c S +I	 dodplm}  |            rdOS n# t&          $ r Y nw xY wt          dqdre          )sa~  
    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glmrV   zz-aizz.aizhipugooglerR   zgoogle-geminizgoogle-ai-studiozx-air   zx.aigrokkimir[   zkimi-for-codingmoonshotzkimi-cnr_   zmoonshot-cnstepra   zstepfun-coding-planzarcee-aird   arceeaiz	gmi-cloudrg   gmicloudzminimax-chinar   
minimax_cnzminimax-portalrn   zminimax-globalminimax_oauthalibaba_codingr}   zalibaba-codingalibaba_coding_planclaudert   zclaude-codegithubrJ   zgithub-copilotzgithub-modelszgithub-modelzgithub-copilot-acprO   zcopilot-acp-agent	aigatewayr   vercelzvercel-ai-gatewayopencoder   zenzqwen-portalrD   qwen-clirE   z
gemini-clizgemini-oauthhfr   zhugging-facezhuggingface-hubmimor   zxiaomi-mimotencentr   tokenhubztencent-cloudtencentmaasawsr   zaws-bedrockzamazon-bedrockamazongor   zopencode-go-subkilor   z	kilo-codezkilo-gatewayrF   z	lm-studiocustomr   )	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(  r  	logged_inz)Could not detect active auth provider: %sNOPENAI_API_KEYOPENROUTER_API_KEYrG   )rJ   rF   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+   r6   agent.bedrock_adapterr)  ImportError)r  r  r  r  _PROVIDER_ALIASES_config_hintmsgr  r  statusepidr   r   r)  s                  r?   resolve_providerr5  z  sf     %v,,..4466J u e %+U 4;U ( +X 7I8  	  u  '-e  		   1-	  BL]	 
 	# 
 &34D  		  1)    	G  '  	U  '  	  (4\  	/  ,<_  O^_n  	/  2BCX  	4  	+   -k  	)  .y  	      %3I   	m!   .A-! " 	\# " $,\# " <OP\# $ 	N% $ %*>% & 	|' & &0' & @L\' & \o  qD' & FR  Tg' & iw  yL' ( 	m) ( ,]) ( =N})     * 	+ * (+ , 	%- , (23E- . 	+/ . .;<N/ 0 	y1 0 (1 0 5Ei1 0 RZ[d1 2 	m3 2 /3 4 	
5 4 (5 4 6DZ5 6 	J7 6 !,Z7   6 GQNhH?     B #&&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G 
G?G::G?)J< <
K	K	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;  replacer   r  	timestamp)r   textparseds      r?   _parse_iso_timestamprB    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)rB  rs  )rC  rD  expires_epochs      r?   _is_expiringrG    s0    (88MtTY[[<788r>   
expires_inc                h    	 t          |           }n# t          $ r d}Y nw xY wt          d|          S )Nr   )r   r   rt  )rH  ttls     r?   _coerce_ttl_secondsrK    sF    *oo   q#;;s    !!c                    t          | t                    sd S |                                                     d          }|r|nd S )N/)r   r(   r   rstrip)r   r   s     r?   _optional_base_urlrO    sC    eS!! tkkmm""3''G'774'r>   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  r  r   =r   r;  )r   r(   countsplitr   base64urlsafe_b64decoder  r  r  decoder   r=   )r   rO  rD  claimss       r?   _decode_jwt_claimsrX    s    eS!! U[[%5%5%:%:	kk#q!Gsq3w<<!++q011G&w~~g'>'>??CJJw//00   			--56625s   +AB: :C	C	r   c                
   t          |           }|                    d          }t          |t          t          f          sdS t	          |          t          j                    t          dt          |                    z   k    S )NexpFr   )rX  r  r   r   r   rs  rt  )r   rD  rW  rZ  s       r?   _codex_access_token_is_expiringr[  +  si    --F
**U

CcC<(( u::$)++As</@/@(A(AABBr>   c                 4    t          j                    dz  dz  S )Nz.qwenzoauth_creds.json)r   rV  r4   r>   r?   _qwen_cli_auth_pathr]  3  s    9;; #555r>   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.rD   qwen_auth_missingr'  r(  r;  rf  z)Failed to read Qwen CLI credentials from : qwen_auth_read_failedz Invalid Qwen CLI credentials in r  qwen_auth_invalid)	r]  rn  r%  r  r  r  r   r   r=   )	auth_pathdatar   s      r?   _read_qwen_cli_tokensrf  7  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 )	NTrc  z.tmpr  )r  rK  r  r;  rf  )r]  rj  rk  r^  rq  r  rN  r   r  ro  r  r  r>  )rg  rd  r  s      r?   _save_qwen_cli_tokensri  P  s    #%%I4$777$$V,,H
6!tDDDtKV]^^^HXt|dl2333Yr>   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   rs  rt  )rj  rD  	expiry_mss      r?   _qwen_access_token_is_expiringrn  Z  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'.rD   qwen_refresh_token_missingr`  !application/x-www-form-urlencodedr   r   Accept
grant_typerq  r/   r  re  r   zQwen OAuth refresh failed: qwen_refresh_failed  z9Qwen OAuth refresh failed. Re-run 'qwen auth qwen-oauth'. Response: z*Qwen OAuth refresh returned invalid JSON: qwen_refresh_invalid_jsonr   z1Qwen OAuth refresh response missing access_token.qwen_refresh_invalid_responserH  i`T  
token_typeBearerresource_urlzportal.qwen.airl  r   )r   rq  r~  r  expiry_date)r(   r  r   r%  r	  r
  QWEN_OAUTH_TOKEN_URLQWEN_OAUTH_CLIENT_IDr   r  r@  r  r   r=   r   rs  rt  ri  )
rg  r`  rq  responser   bodyrO  rH  expires_in_seconds	refresheds
             r?   _refresh_qwen_cli_tokensr  b  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F)force_refreshrefresh_if_expiringrefresh_skew_secondsr  r  r  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'.rD   qwen_access_token_missingr`  HERMES_QWEN_BASE_URLrM  r  )r'  r  rG   r   expires_at_msr  )rf  r(   r  r   r   rn  r  r%  r   r   rN  DEFAULT_QWEN_BASE_URLr]  )r  r  r  rg  r   should_refreshr  s          r?    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,..//  r>   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)r  Tr   rG   r  )r%  r  r   rG   r  r%  r  r4  )r]  r  r(   r  r%  )rd  credsr   s      r?   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"r  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: rE   google_oauth_module_missingr`  Nr  google-oauthr,   )r'  r  rG   r   r  r  email
project_id)agent.google_oauthr  r  r  r  r.  r%  r(   r(  !DEFAULT_GEMINI_CLOUDCODE_BASE_URL
expires_msr  r  )	r  r  r  r  r  r   r   r  r  s	            r?   (resolve_gemini_oauth_runtime_credentialsr    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   )r  r  Fzagent.google_oauth unavailable)r%  r4  Nznot logged inr  Tr  )r%  r  r   rG   r  r  r  )	r  r  r  r.  r   r(   r  r  r  )r  r  rd  r  s       r?   get_gemini_oauth_auth_statusr    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    	raw_scope	List[str]c                    | pt                                           }d |                                D             }t                      }g }|D ]0}||vr*|                    |           |                    |           1|S )Nc                    g | ]}||S r4   r4   ).0parts     r?   
<listcomp>z'_spotify_scope_list.<locals>.<listcomp>-  s    :::tT:d:::r>   )DEFAULT_SPOTIFY_SCOPEr   rS  setaddr  )r  
scope_textscopesseenorderedr0   s         r?   _spotify_scope_listr  +  s    44;;==J::z//11:::FUUDG " "HHUOOONN5!!!Nr>   c                F    d                     t          |                     S )Nr!   )r  r  )r  s    r?   _spotify_scope_stringr  7  s    88'	22333r>   explicitc                   ddl m} |  |d           |d          t          |t                    r|                    d          nd f}|D ]+}t          |pd                                          }|r|c S ,t          ddd	
          )Nr   r   HERMES_SPOTIFY_CLIENT_IDSPOTIFY_CLIENT_IDr/   r,   zPSpotify client_id is required. Set HERMES_SPOTIFY_CLIENT_ID or pass --client-id.r"   spotify_client_id_missingr`  )r   r   r   r=   r  r(   r   r%  r  r   r   
candidates	candidater   s         r?   _spotify_client_idr  ;  s     0///// 	011)**",UD"9"9C		+t	J    	io2&&,,.. 	NNN	
Z(   r>   c                   ddl m} |  |d           |d          t          |t                    r|                    d          nd t
          f}|D ]+}t          |pd                                          }|r|c S ,t
          S )Nr   r   HERMES_SPOTIFY_REDIRECT_URISPOTIFY_REDIRECT_URIredirect_urir,   )r   r   r   r=   r  DEFAULT_SPOTIFY_REDIRECT_URIr(   r   r  s         r?   _spotify_redirect_urir  R  s     0///// 	344,--%/t%<%<F		.!!!$$J    	io2&&,,.. 	NNN	''r>   c                   ddl m}  |d          t          | t                    r|                     d          nd t
          f}|D ]>}t          |pd                                                              d          }|r|c S ?t
          S )Nr   r   HERMES_SPOTIFY_API_BASE_URLapi_base_urlr,   rM  )	r   r   r   r=   r  DEFAULT_SPOTIFY_API_BASE_URLr(   r   rN  r   r   r  r  r   s        r?   _spotify_api_base_urlr  f  s    ////// 	344%/t%<%<F		.!!!$$J
    	io2&&,,..55c:: 	NNN	''r>   c                   ddl m}  |d          t          | t                    r|                     d          nd t
          f}|D ]>}t          |pd                                                              d          }|r|c S ?t
          S )Nr   r    HERMES_SPOTIFY_ACCOUNTS_BASE_URLaccounts_base_urlr,   rM  )	r   r   r   r=   r  !DEFAULT_SPOTIFY_ACCOUNTS_BASE_URLr(   r   rN  r  s        r?   _spotify_accounts_base_urlr  u  s    ////// 	899*4UD*A*AK		%&&&t)J
    	io2&&,,..55c:: 	NNN	,,r>   @   lengthc                    t          j        t          j        |                                         d          }|                    d          d d         S )NasciirQ     )rT  urlsafe_b64encoder   urandomrV  rN  )r  rD  s     r?   _spotify_code_verifierr    sC    

"2:f#5#5
6
6
=
=g
F
FC::c??4C4  r>   code_verifierc                    t          j        |                     d                                                    }t	          j        |                              d                              d          S )Nr;  r  rQ  )r  r  r  digestrT  r  rV  rN  )r  r  s     r?   _spotify_code_challenger    sX    ^M0099::AACCF#F++227;;BB3GGGr>   r/   r  r0   code_challenger  c           
     >    t          | d|||d|d          }| d| S )Nr(  S256)r/   response_typer  r0   r   code_challenge_methodr  z/authorize?)r   )r/   r  r0   r   r  r  querys          r?   _spotify_build_authorize_urlr    sH     $!'(   E  33E333r>   tuple[str, int, str]c                    t          |           }|j        dk    rt          ddd          |j        pd}|dvrt          ddd          |j        st          d	dd          ||j        |j        pd
fS )NhttpzHSpotify PKCE redirect_uri must use http://localhost or http://127.0.0.1.r"   spotify_redirect_invalidr`  r,   >   	127.0.0.1	localhostz?Spotify PKCE redirect_uri must point to localhost or 127.0.0.1.zBSpotify PKCE redirect_uri must include an explicit localhost port.rM  )r   schemer%  hostnameportrY  )r  rA  hosts      r?   _spotify_validate_redirect_urir    s    l##F}V+
 
 
 	

 ? bD---M+
 
 
 	

 ; 
P+
 
 
 	

 fk0S00r>   expected_path3tuple[type[BaseHTTPRequestHandler], dict[str, Any]]c                H     d d d d d G  fddt                     }|fS )N)r(  r   r4  error_descriptionc                  &    e Zd Zd
 fdZddZd	S )?_make_spotify_callback_handler.<locals>._SpotifyCallbackHandlerr   r,  c                   t          | j                  }|j        k    rE|                     d           |                                  | j                            d           d S t          |j                  }|                    dd g          d         d<   |                    dd g          d         d<   |                    dd g          d         d<   |                    dd g          d         d<   |                     d           | 	                    d	d
           |                                  d         rd}nd}| j                            |
                    d                     d S )N  s
   Not found.r(  r   r   r4  r  r  r   ztext/html; charset=utf-8zW<html><body><h1>Spotify authorization failed.</h1>You can close this tab.</body></html>zY<html><body><h1>Spotify authorization received.</h1>You can close this tab.</body></html>r;  )r   rY  send_responseend_headerswfiler  r   r  r  send_headerr  )r1  rA  paramsr  r  results       r?   do_GETzF_make_spotify_callback_handler.<locals>._SpotifyCallbackHandler.do_GET  si   di((F{m++""3'''  """
  ///fl++F#ZZ77:F6N$jj4&99!<F7O$jj4&99!<F7O*0**5H4&*Q*QRS*TF&'s###^-GHHHg sprJT[[1122222r>   formatr(   argsr   c                    d S r.  r4   )r1  r  r   s      r?   log_messagezK_make_spotify_callback_handler.<locals>._SpotifyCallbackHandler.log_message  s    Fr>   Nr   r,  )r  r(   r   r   r   r,  )r8   r9   r:   r  r  )r  r  s   r?   _SpotifyCallbackHandlerr    sL        	3 	3 	3 	3 	3 	3 	3.	 	 	 	 	 	r>   r  )r	   )r  r  r  s   ` @r?   _make_spotify_callback_handlerr    sc    !	 F       "8   6 #F**r>        f@r`  dict[str, Any]c          	        t          |           \  }}}t          |          \  }} G d dt                    }	  |||f|          }n.# t          $ r!}	t	          d| d| d|	 dd          |	d }	~	ww xY wt          j        |j        d	d
id          }
|
                                 t          j	                    t          d|          z   }	 t          j	                    |k     r{|d         s|d         r@||                                 |                                 |
                    d           S t          j        d
           t          j	                    |k     {|                                 |                                 |
                    d           nC# |                                 |                                 |
                    d           w xY wt	          ddd          )Nc                      e Zd ZdZdS )4_spotify_wait_for_callback.<locals>._ReuseHTTPServerTN)r8   r9   r:   allow_reuse_addressr4   r>   r?   _ReuseHTTPServerr    s        "r>   r  z*Could not bind Spotify callback server on :ra  r"   spotify_callback_bind_failedr`  poll_intervalg?T)r  kwargsdaemon      @r(  r4  rh  r   z?Spotify authorization timed out waiting for the local callback.spotify_callback_timeout)r  r  r
   r}  r%  	threadingThreadserve_foreverstartrs  rt  shutdownserver_closer  r  )r  r`  r  r  rY  handler_clsr  r  serverr   threadr  s               r?   _spotify_wait_for_callbackr    s>   
 6lCCD$8>>K# # # # #: # # #!!4,<<   MMMMMMM/
 
 
 		 V%9?TWBXaefffF
LLNNNy{{So666H!ikkH$$f~   	C    	 JsOOO ikkH$$
 	C     	C    
I'   s*   A	 	
A4A//A4(F 5+F A G)previous_statetoken_payloadrequested_scoper  r   c               t   t          j        t          j                  }t	          |                     dd                    }t          j        |                                |z   t          j                  }	t          |pi           }
|
	                    |||||t          |                     d          p|                                          t          |                     dd          pd                                          pdt          |                     dd          pd                                          t          |                     d	          p|
                    d	          pd                                          |                                |	                                |d
d           |
S )NrH  r   tzr0   r~  r  r   r,   rq  
oauth_pkce)r/   r  r  r  r0   granted_scoper~  r   rq  obtained_at
expires_atrH  r+   )r   r  r   r  rK  r  fromtimestampr?  r=   rM  r(   r   r  )r!  r/   r  r"  r  r  r   r  rH  r)  r   s              r?   _spotify_token_payload_to_stater+    s    ,x|
$
$C$]%6%6|Q%G%GHHJ'*(DVVVJ%2&&E	LL$.$ ]..w77J?KKQQSS-++L(CCOxPPVVXXd\dM--nbAAGRHHNNPPo.. yy))
 
 %''}} **,, !#    & Lr>   r(  c           
        	 t          j        | dddi| d|||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	
          |                                }	t          |	t                    r7t          |	                    dd          pd                                          st          ddd
          |	S )N
/api/tokenr   rs  authorization_code)r/   rw  r(  r  r  rx  zSpotify token exchange failed: r"   spotify_token_exchange_failedr`  rz  zSpotify token exchange failed.r{  r,   r   z7Spotify token response did not include an access_token.spotify_token_exchange_invalid)r	  r
  r   r%  r  r@  r   r  r   r=   r(   r  )
r/   r(  r  r  r  r`  r  r   detailrO  s
             r?   !_spotify_exchange_code_for_tokensr2  ,  su   : ,,,#%HI&2 ,!.  $
 
 
    3c330
 
 
 		 s""$$&&,)/7%V%%%R90	
 
 
 	
 mmooGgt$$ 
CNB0O0O0USU,V,V,\,\,^,^ 
E1
 
 
 	

 Ns   #& 
AAAc          
        t          |                     dd          pd                                          }|st          dddd          t	          |           }t          |           }	 t          j        | d	d
did||d|          }n(# t          $ r}t          d| dd          |d }~ww xY w|j	        dk    r6|j
                                        }t          d|rd| ndz   ddd          |                                }t          |t                    r7t          |                    dd          pd                                          st          dddd          t          ||t          |           t          |                     d          pt                     |t#          |           |           S )Nrq  r,   z?Spotify refresh token missing. Run `hermes auth spotify` again.r"   spotify_refresh_token_missingTr&  r   r-  r   rs  rv  rx  zSpotify token refresh failed: spotify_refresh_failedr`  rz  z>Spotify token refresh failed. Run `hermes auth spotify` again.r{  r   z9Spotify refresh response did not include an access_token.spotify_refresh_invalidr0   )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`  rq  r/   r  r  r   r1  rO  s	            r?   _refresh_spotify_oauth_stater8  [  sB   
 		/266<"==CCEEM 
M0!	
 
 
 	
 #///I2599: ,,,#%HI-!.& 
 $	
 	
 	
    2S22)
 
 
 		 s""$$&&L)/7%V%%%R9)!
 
 
 	
 mmooGgt$$ 
CNB0O0O0USU,V,V,\,\,^,^ 
G*!	
 
 
 	
 +*777EIIg..G2GHH+*511   s   -!B 
B4B//B4c                   t                      5  t                      }t          |d          }|st          dddd          t	          |           }|s%|r#t          |                    d          |          }|r1t          |          }t          |d|d           t          |           d d d            n# 1 swxY w Y   t          |                    d	d
          pd
                                          }|st          dddd          d||t          |                    dd          pd          t          |          t          |                    d          p|                    d          pd
                                          t          |          t          |          |                    d          t          |                    dd
          pd
                                          d
S )Nr"   z>Spotify is not authenticated. Run `hermes auth spotify` first.spotify_auth_missingTr&  r)  Fr  r   r,   z>Spotify access token missing. Run `hermes auth spotify` again.spotify_access_token_missingr~  r  r'  r0   r5  rq  )
r'  r   rG   r~  r  r0   r/   r  r)  rq  )r  r  r  r%  r   rG  r  r8  r  r  r(   r   r  r  r  )r  r  r  r  r   r  r   s          r?   #resolve_spotify_runtime_credentialsr<    sK    
		 ) )%''
$Z;; 	P"+!%	    m,, 	Y"5 	Y)%))L*A*ACWXXN 	)077E!*i5QQQQZ(((#) ) ) ) ) ) ) ) ) ) ) ) ) ) )& uyy44:;;AACCL 
L/!	
 
 
 	
 $%))L(;;GxHH)%00UYY//K599W3E3EKLLRRTT'e444-E:::ii--UYY;;ArBBHHJJ  s   BB88B<?B<c            
        t          d          } | sddiS |                     d          }t          |                     dd          pd                                          }t	          |pt          |d                     |                     dd	          |                     d
          |                     d          |                     d          p|                     d          ||                     d          t	          |          dS )Nr"   r%  Fr)  rq  r,   r   r+   r&  r/   r  r'  r0   r  )r%  r+   r/   r  r0   r)  r  has_refresh_token)r  r  r(   r   r   rG  )r   r)  rq  s      r?   get_spotify_auth_statusr?    s    #I..E $U##<((J		/266<"==CCEEM-J|J/J/J+JKKYY{L99YY{++		.11?++Auyy/A/A 		.11!-00	 	 	r>   redirect_uri_hintc                   ddl m} t                       t          d           t          d           t          d           t                       t          d           t          d           t          d           t                       t          dt                      t                       t          d	           t          d
t           d           t          d           t          d           t          d           t          d|             t          d           t          d           t          d           t          d           t                       t                      s+	 t          j        t                     n# t          $ r Y nw xY w	 t          d          
                                }n2# t          t          f$ r t                       t          d          w xY w|s5t                       t          dt           d           t          d           |d|           | r| t          k    r |d|            t                       t          d           t                       |S )zWalk the user through creating a Spotify developer app, persist the
    resulting client_id to ~/.hermes/.env, and return it.

    Raises SystemExit if the user aborts or submits an empty value.
    r   )save_env_valuezF======================================================================zSpotify first-time setupz=Spotify requires every user to register their own lightweightz>developer app. This takes about two minutes and only has to bezdone once per machine.zFull guide: zSteps:z  1. Opening z in your browser...z$  2. Click 'Create app' and fill in:z1       App name:     anything (e.g. hermes-agent)z       Description:  anythingz       Redirect URI: z       API/SDK:      Web APIz$  3. Agree to the terms, click Save.z9  4. Open the app's Settings page and copy the Client ID.z  5. Paste it below.zSpotify Client ID: zSpotify setup cancelled.zNo Client ID entered. See z for the full guide.z)Spotify setup cancelled: empty Client ID.r  r  z0Saved HERMES_SPOTIFY_CLIENT_ID to ~/.hermes/.env)r   rB  printSPOTIFY_DOCS_URLSPOTIFY_DASHBOARD_URL_is_remote_session
webbrowserrr  r   inputr   EOFErrorKeyboardInterrupt
SystemExitr  )r@  rB  rD  s      r?   _spotify_interactive_setuprL    s    100000	GGG	(OOO	
$%%%	(OOO	GGG	
IJJJ	
JKKK	
"###	GGG	
+)
+
+,,,	GGG	(OOO	
D/
D
D
DEEE	
0111	
=>>>	
)***	
5"3
5
5666	
()))	
0111	
EFFF	
 !!!	GGG 	O12222 	 	 	D	5)**0022'( 5 5 534445  FQ+;QQQRRRDEEE N-s333  I.2NNN46GHHH	GGG	
<===	GGGJs   /F	 	
FF!F< </G+c                   t          d          pi }t          | dd           }	 t          ||          }nS# t          $ rF}t          |dd          dk    r t	          t          | dd           pt
                    }Y d }~nd }~ww xY wt          t          | dd           |          }t          t          | dd           p|                    d                    }t          |          }t          |          }t          | d	d
           }	t                      }
t          |
          }t          j                    j        }t!          ||||||          }t#          d           t#          d|            t#          d|            t#          d           t#                       t#          d           t#          |           t#                       t#          dt$                      t#                       |	rWt'                      sI	 t)          j        |          }n# t,          $ r d
}Y nw xY w|rt#          d           nt#          d           t/          |t1          t          | dd           pd                    }|                    d          r/|                    d          p|d         }t3          d|           |                    d          |k    rt3          d          t5          |t7          |                    d          pd          ||
|t1          t          | dd           pd                    }t9          ||||||          }t;                      5  t=                      }t?          |d|d
           tA          |          }d d d            n# 1 swxY w Y   t#          d            t#          d!|            t#          d"           t#          d#t$                      d S )$Nr"   r/   r(  r,   r  r  )r@  r0   
no_browserF)r/   r  r0   r   r  r  zStarting Spotify PKCE login...zClient ID: zRedirect URI: zIMake sure this redirect URI is allow-listed in your Spotify app settings.z"Open this URL to authorize Hermes:zFull setup guide: z)Browser opened for Spotify authorization.z<Could not open the browser automatically; use the URL above.r   r  r  r4  r  zSpotify authorization failed: r   z-Spotify authorization failed: state mismatch.ro  )r/   r(  r  r  r  r`  )r/   r  r"  r  r  r  zSpotify login successful!  Auth state: z.  Provider state saved under providers.spotifyz  Docs: )!r  r   r  r%  rL  r  r  r  r  r  r  r  r  r  r  r  r  rC  rD  rF  rG  rr  r   r  r   rK  r2  r(   r+  r  r  r  r  )r   existing_stateexplicit_client_idr/   r   r  r0   r  r  open_browserr  r  state_nonceauthorize_urlopenedcallbackr1  r!  spotify_stater  saved_tos                        r?   login_spotify_commandrY    s   ,Y77=2N
 !{D99
&'9>JJ		 
 
 
3##'BBB.%dNDAAaEa
 
 
						
 )~t)L)Ln]]L!'$">">"].BTBTU\B]B]^^E2>BB(88Lt\5999L*,,M,];;N*,,"K0!%+  M 

*+++	
#	
#
#$$$	
)<
)
)***	
UVVV	GGG	
.///	-	GGG	
1/
1
1222	GGG R.00 R	_]33FF 	 	 	FFF	 	R=>>>>PQQQ)gdIt<<EFF  H ||G D122Ghw6GB&BBCCC||G++HIII5f%%+,,!#+gdIt<<DEE  M 4!+!  M 
		 0 0%''
j)]uUUUU#J//0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 

%&&&	
%8
%
%&&&	
:;;;	
'%
'
'(((((s8   5 
B<B  BH H*)H*!1NN"%N"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   r4   r>   r?   rF  rF  {  s)    	,''?29Y+?+?@@@r>   _lockr^  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.
    NrB   z?No Codex credentials stored. Run `hermes auth` to authenticate.codex_auth_missingTr&  rg  zICodex auth state is missing tokens. Run `hermes auth` to re-authenticate.codex_auth_invalid_shaper   rq  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)rg  re  )	r  r  r  r%  r  r   r=   r(   r   )r^  r  r   rg  r   rq  s         r?   _read_codex_tokensrf    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re  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:  r8  rB   rg  re  chatgpt	auth_mode)r   r  r   r  r  r>  r  r  r  r  r  )rg  re  r  r   s       r?   _save_codex_tokensrj    s   |HL11;;==EEhPSTT			 % %%''
$Z@@FB h ,n&kZ???$$$% % % % % % % % % % % % % % % % % %s   AB22B69B6rq  c          	     T   ~ 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|
                    d          p|
                    d          }t          |t                    r(|                                r|                                }|
                    d          }t          |t                    r+|                                rd|                                 }nt          |
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}|j        dv r|s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.rc  rB   rd  Tr&  r  ru  r   r   r  r   rs  rq  rv  )r  re  Nr  codex_refresh_failedz'Codex token refresh failed with status r  Fr4  r(  typer*  zCodex token refresh failed: r  >   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.)i    z*Codex token refresh returned invalid JSON.codex_refresh_invalid_jsonr   z6Codex token refresh response was missing access_token."codex_refresh_missing_access_tokenr:  r8  )r   rq  re  )r   r(   r   r%  r	  Timeoutrt  r   Clientr
  CODEX_OAUTH_TOKEN_URLCODEX_OAUTH_CLIENT_IDr  r  r=   r  r   r   r  r   r  r  r>  )r   rq  r`  r   clientr  r(  r*  r)  errerr_objnested_code
nested_msgerr_descrefresh_payloadr   refreshed_accessupdatednext_refreshs                      r?   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$$ T'''**gt,, T")++f"5"5"LV9L9LK!+s33 38I8I8K8K 3*0022!(Y!7!7J!*c22 Vz7G7G7I7I V"UAQAQASAS"U"U-- T'--// T"==??D"ww':;;Qswwy?Q?QH!(C00 TX^^5E5E T"SAQAQ"S"S 	 	 	D	HHH#)))=   $ :--6F-##-	
 
 
 	
"--//   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>!F8J 
J'&J'K0 0
L:LLc           	        t          t          |                     dd          pd          t          |                     dd          pd          |          }t          |           }|d         |d<   |d         |d<   t	          |           |S )zzRefresh Codex access token using the refresh token.
    
    Saves the new tokens to Hermes auth store automatically.
    r   r,   rq  r  )r  r(   r  r=   rj  )rg  r`  r  updated_tokenss       r?   _refresh_codex_auth_tokensr  /	  s     )FJJ~r**0b11FJJ++1r22'  I
 &\\N%.~%>N>"&/&@N?#~&&&r>   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.
    
CODEX_HOMEr,   z.codexrR  Nrg  r   rq  r   u7   Codex CLI tokens at %s are expired — skipping import.)r   r   r   r(   r   rV  
expanduseris_filer  r  r  r  r   r=   r[  r   r  r   )
codex_homerd  rO  rg  r   rq  s         r?   _import_codex_cli_tokensr  D	  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                      }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.rg  r   r,   $HERMES_CODEX_REFRESH_TIMEOUT_SECONDS20r  r  Fr]  NHERMES_CODEX_BASE_URLrM  rB   zhermes-auth-storere  rh  )r'  r  rG   r   re  ri  )rf  r=   r(   r  r   r   r   r   r   r[  r  rt  AUTH_LOCK_TIMEOUT_SECONDSr  rN  DEFAULT_CODEX_BASE_URL)	r  r  r  re  rg  r   refresh_timeout_secondsr  r  s	            r?   !resolve_codex_runtime_credentialsr  f	  sZ    D$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   C	F++F/2F/bool | ssl.SSLContextc                     t           j        dk    r<	 ddl} t          j        |                                           S # t          $ r Y nw xY wdS )a~  Platform-aware default SSL verify for httpx clients.

    On macOS with Homebrew Python, the system OpenSSL cannot locate the
    system trust store and valid public certs fail verification. When
    certifi is importable we pin its bundle explicitly; elsewhere we
    defer to httpx's built-in default (certifi via its own dependency).
    Mirrors the weixin fix in 3a0ec1d93.
    darwinr   NcafileT)sysplatformcertifisslcreate_default_contextwherer.  )r  s    r?   _default_verifyr  	  s`     |x	NNN-W]]__EEEE 	 	 	D	4s   *= 
A
	A
insecure	ca_bundle
auth_stater  Optional[bool]r  r  c                   t          |t                    r|                    d          ni }t          |t                    r|ni }| t          | d          n$t          |                    dd          d          }|pP|                    d          p;t	          j        d          p't	          j        d          pt	          j        d          }|rdS |rlt          |          }t          j                            |          s)t          
                    d	|           t                      S t          j        |
          S t                      S )NtlsFdefaultr  r  HERMES_CA_BUNDLESSL_CERT_FILEREQUESTS_CA_BUNDLEuJ   CA bundle path does not exist: %s — falling back to default certificatesr  )r   r=   r  r   r   r   r(   rY  isfiler   r   r  r  r  )r  r  r  	tls_stateeffective_insecureeffective_caca_paths          r?   _resolve_verifyr  	  s\    *4J)E)EM
u%%%2I'	488@		bI 5=4H%0000Y]]:u==uMMM 
 	 	+==%%	+9'((	+ 9_%%	+ 9)**   u :l##w~~g&& 	%NN\   #$$$)9999r>   rz  httpx.Clientr-   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/   r0   re  )device_code	user_codeverification_uriverification_uri_completerH  intervalc                    g | ]}|v|	S r4   r4   )r  fre  s     r?   r  z(_request_device_code.<locals>.<listcomp>	  s    ;;;QQd]]q]]]r>   z%Device code response missing fields: z, )r
  raise_for_statusr  r   r  )rz  r-   r/   r0   r  required_fieldsmissingre  s          @r?   _request_device_coder  	  s     {{222
#(0b
   H ==??D  O <;;;/;;;G WU7ASASUUVVVKr>   r  r  c                &   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)rw  r/   r  r  r  r   z+Token response did not include access_tokenz1Token endpoint returned a non-JSON error responser4  r,   authorization_pending	slow_down   r  zUnknown authentication errorra  z*Timed out waiting for device authorization)rs  rt  min%DEVICE_AUTH_POLL_INTERVAL_CAP_SECONDSr
  r  r  r   r   r  rX  r  r  r  )rz  r-   r/   r  rH  r  r  current_intervalr  rO  error_payload
error_codedescriptions                r?   _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 }
|	                                }d|v sd|v rd}	t          |	d||
          )Nr  rq  rw  r/   rq  r  r  r   z%Refresh response missing access_tokenr@   rp  Tr&  zRefresh token exchange failedr'  r)  r4  ro  r     ro  rp  reusezreuse detectedu  Nous Portal detected refresh-token reuse and revoked this session.
This usually means an external process (monitoring script, custom self-heal hook, or another Hermes install sharing ~/.hermes/auth.json) called POST /api/oauth/token with Hermes's refresh token without persisting the rotated token back.
Nous refresh tokens are single-use — only Hermes may call the refresh endpoint. For health checks, use `hermes auth status` instead.
Re-authenticate with: hermes auth add nous)r
  r  r  r%  r   r(   r  r   )rz  r-   r/   rq  r  rO  r  r   r(  r  reloginlowereds               r?   _refresh_access_tokenr   
  s    {{,,,)"*
 
   H s""--//((C%+/TXZ Z Z ZI  I I I7!'$@ @ @EH	II }  /::;;Dm''(;<<_@_``K88G !!G'-889 	 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  rG   zMint response missing api_keyr@   server_errorr`  zAgent key mint request failedNr4  r  r  r&  )	r
  rt  r   r  r  r%  r   r(   r  )rz  r-   r   r  r  rO  r  r   r(  r  r  s              r?   _mint_agent_keyr  Y
  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  
bool | strc                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.ru  r   r   r  r  rM  z/modelsr   r   )r  Nr  z#/models request failed with status r  r4  z'Could not parse error response JSON: %sr@   models_fetch_failedr`  re  r)   hermesmidr(   r   r5   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      r  )r   )r  lows     r?   _model_priorityz*fetch_nous_models.<locals>._model_priority
  sX    iikkS==s8OC<<HC//s8Os??s8O3xr>   r   )r  r(   r   r5   )r	  rv  rw  r  rN  r  r  r(   r   r   r  r%  r   r  r=   r   r   r  sortfromkeys)r.   rG   r`  r  r   rz  r  r  r{  r3  rO  re  	model_idsitemmodel_idr  r  s                    r?   fetch_nous_modelsr  z
  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   rG  )r   r  r   s      r?   _agent_key_is_usabler  
  sY    
))K
 
 Cc3 syy{{ uEII&<==OOOOr>   )r`  r  r  r  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.r@   &Hermes is not logged into Nous Portal.Tr  r-   HERMES_PORTAL_BASE_URLNOUS_PORTAL_BASE_URLrM  r/   r  r   rq  ,No access token found for Nous Portal login.r)  N2Session expired and no refresh token is available.r   ru  r   r  rz  r-   r/   rq  rH  r~  r  r0   r(  r$  Fr  r  r  )r  r  r  r%  rO  r  r   r   DEFAULT_NOUS_PORTAL_URLrN  r(   DEFAULT_NOUS_CLIENT_IDr  r   rG  r	  rv  rw  r  r   r  r   r  rK  r  r*  r?  r  r  )r`  r  r  r  r  r   r-   r/   r  r   rq  r   rz  r  r  
access_ttls                   r?   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~  r0   r(  r)  r  r  min_key_ttl_secondsr`  r  r  r  
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.rM  r  r  )r   rq  r/   r-   r.   r~  r0   r(  r)  r  r  r  r  r   ru  r   r  r)  r-   r/   rq  r  rH  r   r~  r0   r.   r(  r$  r   rz  r-   r   r  rG   r  key_idagent_key_idr  agent_key_expires_inreusedFagent_key_reusedagent_key_obtained_atN)r  r  rN  DEFAULT_NOUS_INFERENCE_URLDEFAULT_NOUS_SCOPEr   r  r	  rv  rw  rG  r  !ACCESS_TOKEN_REFRESH_SKEW_SECONDSr  r   r  r   r  rK  rO  r  r*  r?  r  rt  r   r  )r   rq  r/   r-   r.   r~  r0   r(  r)  r  r  r   r`  r  r  r  r  r   r  r   rz  r  r  r  refreshed_urlmint_payload
minted_urls                              r?   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`  r  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,   rq  r/   r   r-   r.   r~  r  r0   r(  r)  r  r  r  r  r  )r  r  r  r
  r  )r   r   r`  r  r  r  s         r?   refresh_nous_oauth_from_stater  b  s    ))E


 bC"		."%%		/2&&		+|,,		#%<==		&(BCC99\844ii!344IIm,,99\**))K(("YY'=>>/'$$''+&&##   r>   )r  r  r  c                  ddl m} t          |           }|rEt          |                                          r$t          |                                          |d<   t                      5  t                      }t          |d|           t          |           ddd           n# 1 swxY w Y    |d          }t          d |
                                D             d          S )u4  Persist minted Nous OAuth credentials as the singleton provider state
    and ensure the credential pool is in sync.

    Nous credentials are read at runtime from two independent locations:

    - ``providers.nous``: singleton state read by
      ``resolve_nous_runtime_credentials()`` during 401 recovery and by
      ``_seed_from_singletons()`` during pool load.
    - ``credential_pool.nous``: used by the runtime ``pool.select()`` path.

    Historically ``hermes auth add nous`` wrote a ``manual:device_code`` pool
    entry only, skipping ``providers.nous``.  When the 24h agent_key TTL
    expired, the recovery path read the empty singleton state and raised
    ``AuthError`` silently (``logger.debug`` at INFO level).

    This helper writes ``providers.nous`` then calls ``load_pool("nous")`` so
    ``_seed_from_singletons`` materialises the canonical ``device_code`` pool
    entry from the singleton.  Re-running login upserts the same entry in
    place; the pool never accumulates duplicate device_code rows.

    ``label`` is an optional user-chosen display name (from
    ``hermes auth add nous --label <name>``).  It gets embedded in the
    singleton state so that ``_seed_from_singletons`` uses it as the pool
    entry's label on every subsequent ``load_pool("nous")`` instead of the
    auto-derived token fingerprint.  When ``None``, the auto-derived label
    via ``label_from_token`` is used (unchanged default behaviour).

    Returns the upserted :class:`PooledCredential` entry (or ``None`` if
    seeding somehow produced no match — shouldn't happen).
    r   r   r  r@   Nc              3  :   K   | ]}|j         t          k    |V  d S r.  )r   NOUS_DEVICE_CODE_SOURCE)r  r3  s     r?   	<genexpr>z+persist_nous_credentials.<locals>.<genexpr>  s/      JJqah2I&I&I&I&I&I&IJJr>   )r   r   r=   r(   r   r  r  r  r  nextr  )r  r  r   r   r  r   s         r?   persist_nous_credentialsr    s)   F 0/////KKE ,U!!## ,U))++g			 % %%''
Z777$$$% % % % % % % % % % % % % % %
 9VDJJDLLNNJJJ  s   +/B&&B*-B*)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<  r@   r  Tr  r-   r  r  rM  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 )Nr@   nous_state_persist_failed)rG  r  
error_typenous_state_persistedrq  r   )rG  r  refresh_token_fpaccess_token_fp)r  r  r   rP  rn  r8   r=  r  )r  r   r  rG  r   s     r?   _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_startrq  )rG  r  r   r!  ru  r   r  r   r  r)  r  refresh_startaccess_expiring)rG  r  r!  r  rH  r~  r  r0   r(  r$  refresh_success)rG  r  previous_refresh_token_fpnew_refresh_token_fppost_refresh_access_expiringFagent_key_reuserF  
mint_start)rG  r"  r  
mint_error)rG  r(  r  mint_retry_after_invalid_tokenpost_refresh_mint_retryrG   r  r  r  r  r  r  r  r	  mint_success)rG  r  r  r  &resolve_nous_runtime_credentials_finalz*Failed to resolve a Nous inference API keyr  r`  r   cacheportal)r'  r  rG   r  r)  rH  r   )r  r(   r   r,  )*rt  r   r  r  r  r  r  r  r%  rO  r  r   r   r  rN  r
  r(   r  r  r	  rv  rP  r   r=  rw  r   rG  r  r  r   r  r   r  rK  r  r*  r?  r  r  r(  rB  rs  )r   r`  r  r  r  r-   r.   r/   r#  r  r   rz  r   rq  r  r  r  previous_refresh_tokenr  used_cached_keyr  r   latest_refresh_tokenr  rG   r)  rF  rH  r  rG  r   s                               @@@r?    resolve_nous_runtime_credentialsr7    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                     dd d d d ddS )NF)r%  r-   r.   access_expires_atr  r>  r4   r4   r>   r?   _empty_nous_auth_statusr:    s#    "! $"  r>   c                    	 ddl m}   | d          }|r|                                st                      S t	          |                                          }|st                      S dd}t          ||	          }t          |d
d          pt          |dd          }|st                      S d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t          |dd           dS # t          $ r t                      cY S w xY w)zBest-effort status from the credential pool.

    This is a fallback only. The auth-store provider state is the runtime source
    of truth because it is what ``resolve_nous_runtime_credentials()`` refreshes
    and mints against.
    r   r   r@   r   r   r   tuple[float, float, int]c                    t          t          | dd                     pd}t          t          | dd                     pd}t          t          | dd          pd          }||| fS )Nr          r)  priorityr   )rB  r   r   )r   	agent_exp
access_expr?  s       r?   _entry_sort_keyz3_snapshot_nous_pool_status.<locals>._entry_sort_key  so    ,WU<RTX-Y-YZZa^aI-ge\4.P.PQQXUXJ75*a88=A>>HzH955r>   r  r   Nr   r,   Tr-   r  r.   r)  r  rq  pool:r  unknown)r%  r-   r.   r   r9  r  r>  r   )r   r   r   r<  )
r   r   r   r:  r  r  rt  r   r   r   )r   r   r  rB  r   r   s         r?   _snapshot_nous_pool_statusrE    s   &)333333y   	-4//11 	-*,,,t||~~&& 	-*,,,	6 	6 	6 	6 G111E>400 5u/44 	  	-*,,, &u.?FF  0uj$//")%1Et"L"L #0uj$//(!(d!C!C$+E3I4$P$P!%ge_d&K&K!L!LBgeWi@@BB
 
 	
  ) ) )&((((()s$   4E 0E (AE /BE E! E!c                    t          d          } | rkt          |                     d                    |                     d          |                     d          |                     d          |                     d          t          |                     d                    |                     d          dd	}	 t          d
          }t          d          p| }|                    d|                    d          p|                    d          |                    d          p)|                    d          p|                    d          |                    d          p|                    d          |                    d          p)|                    d          p|                    d          t          |                    d                    d|                    dd           |                    d          d           |S # t
          $ r^}|                    dt          |          t          t          |dd                    t          |dd          d           |cY d}~S d}~ww xY wt                      S )a  Status snapshot for Nous auth.

    Prefer the auth-store provider state, because that is the live source of
    truth for refresh + mint operations. When provider state exists, validate it
    by resolving runtime credentials so revoked refresh sessions do not show up
    as a healthy login. If provider state is absent, fall back to the credential
    pool for the just-logged-in / not-yet-promoted case.
    r@   r   r-   r.   r)  r  rq  r  )r%  r-   r.   r9  r  r>  r   r   r   )r   Tr  r9  zruntime:r   r3  r  )r%  r-   r.   r9  r  r>  r   r  Fr)  r(  N)r%  r4  r)  r  )	r  r   r  r7  rM  r%  r(   r   rE  )r   base_statusr  refreshed_stater   s        r?   get_nous_auth_statusrI    s    $F++E &eii7788$yy):;;"')),@"A"A!&<!8!8$)II.D$E$E!%eii&@&@!A!A!IIn55"	
 	
	4LLLE5f==FO!%'6':':;L'M'M'sQ\Q`Q`arQsQs*/))J*?*? +=&**+?@@+="';<<)8)<)<\)J)J)rkoo^qNrNr,1IIl,C,C -?&**+ABB-?"'=>>)-o.A.A/.R.R)S)SH8X)F)FHH#ii11      	 	 	"S$(6H%)P)P$Q$Q%c6488	       	 &'''s    >EH 
I;AI60I;6I;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   rB   Nr   r   r,   Tre  rh  rC  r  rD  )r%  r  re  ri  r   rG   ri  r   rG   F)r%  r  r4  )r   r   r   selectr   r[  r(   r\  r   r  r  r%  )r   r   r   rG   r  r   s         r?   get_codex_auth_statusrL  &  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).rG   
configuredFr,   r[   r_   )rN  r'  r*   
key_sourcer  r%  )r   r  r+   r   r7   r   r   r   r   r.   r   r*   r   r   rG   rP  env_urlr  s         r?   get_api_key_provider_statusrS  W  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 ']]  r>   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.rP   rN  FHERMES_COPILOT_ACP_COMMANDr,   COPILOT_CLI_PATHrJ   HERMES_COPILOT_ACP_ARGS--acp--stdioN
acp+tcp://)rN  r'  r*   commandr   resolved_commandr  r%  )r   r  r+   r   r   r   shlexrS  r7   r.   r  whichr   r   r*   )r   r   r[  raw_argsr   r  r\  s          r?   $get_external_process_provider_statusr`  v  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	 	 	r>   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 |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.r"   r@   rB   rD   rE   rO   rG   r   r   r(  r%  r'  Fzboto3 not installed)r%  r'  r4  r%  )r  r?  rI  rL  r  r  r`  r   r  r+   rS  r-  r)  r.  )r   r  r   r)  s       r?   r+  r+    s\   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! !C43C4c                    t                               |           }|r|j        dk    rt          d|  d| d          d}d}t	          | |          \  }}|s| dk    rt
          }|pd}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.
    rG   
Provider 'z' is not an API-key provider.r$  r`  r,   rF   r  rO  rV   rM  r'  rG   r  r   )r   r  r+   r%  r   LMSTUDIO_NOAUTH_PLACEHOLDERr7   r   r   r   r   r.   r#  rN  rQ  s         r?   $resolve_api_key_provider_credentialsrg    s_   
  ##K00G 
g'944CCCC #
 
 
 	
 GJ:;PPGZ
  -{j00-,9
G B)G4b99??AA777)'73MwWW			('2LgVV	 .>>#&&-  OOC(()		  r>   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.rP   rd  z&' is not an external-process provider.r$  r`  r,   rU  rV  rJ   rW  rX  rY  NrZ  z(Could not find the Copilot CLI command 'zQ'. Install GitHub Copilot CLI or set HERMES_COPILOT_ACP_COMMAND/COPILOT_CLI_PATH.missing_copilot_clirO   rM  process)r'  rG   r  r[  r   r   )r   r  r+   r%  r7   r   r   r   r.   r]  rS  r  r^  r   rN  )r   r   r  r[  r_  r   r\  s          r?   -resolve_external_process_provider_credentialsrk    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  r>   default_modelc                ^   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           |                    d
d           |                    dd           |r!|                    dd          }|rd|v r||d<   ||d<   t          ||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).
    r  NTrc  r   r  r'  rM  r  rG   api_moder,   FrK  )r  r  r  r   rj  rk  r   r  r   r=   r(   r   rN  r  r   )	r   r.   rl  r  config_pathconfigcurrent_modelr  cur_defaults	            r?   _update_config_for_providerrt  
  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$''' MM)T"""MM*d###
  1mmIr22 	1c[00#0Ii F7Ok6U;;;;s   #>AAc                 P   	 t                      } n# t          $ r Y dS w xY w| sdS |                     d          }t          |t                    sdS |                    d          }t          |t
                    sdS |                                                                }|pdS )z?Return model.provider from config.yaml, normalized, if present.Nr   r'  )r   r   r  r   r=   r(   r   r   )rq  r   r'  s      r?   _get_config_providerrv  L  s     ""   tt tJJwEeT"" tyy$$Hh$$ t~~%%''Hts    
c                v    | sdS t                      |                                                                 k    S )z=Return True when config.yaml currently selects *provider_id*.F)rv  r   r   )r   s    r?   _config_provider_matchesrx  ^  s8     u!![%6%6%8%8%>%>%@%@@@r>   c                 .    t                      } | dv r| S dS )a  Fallback logout target when auth.json has no active provider.

    `hermes logout` historically keyed off auth.json.active_provider only.
    That left users stuck when auth state had already been cleared but
    config.yaml still selected an OAuth provider such as openai-codex for the
    agent model: there was no active auth provider to target, so logout printed
    "No provider is currently logged in" and never reset model.provider.
    >   r@   rB   N)rv  )r'  s    r?   $_logout_default_provider_from_configrz  e  s%     $%%H+++4r>   c                    t                      } |                                 s| S t                      }|s| S |                    d          }t	          |t
                    rd|d<   d|v r
t          |d<   t          | |d           | S )z5Reset config.yaml provider back to auto after logout.r   r  r'  r  Fro  )r   rn  r   r  r   r=   r   r   )rp  rq  r   s      r?   _reset_config_providerr|  t  s    !##K F JJwE% 4"j 3E*k6U;;;;r>   r  rr  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  mr}  s     r?   r  z*_prompt_model_selection.<locals>.<genexpr>  s-      &J&J!w{{1~~&J&J&J&J&J&Jr>   c              3  4   K   | ]}t          |          V  d S r.  )r   )r  r  s     r?   r  z*_prompt_model_selection.<locals>.<genexpr>  s(      //qCFF//////r>   r  r  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 )Nr  r!   >  <u     ← currently in user  )r  inpoutr2  
price_partbase_price_cache	cache_colrr  	has_cachehas_pricingname_col	price_cols         r?   _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r>   zSelect default model:z     r  r  r!   Inr  OutCachez  /Mtokz[2mz[0m)TerminalMenuc                ,    g | ]}d  |           S )r  r4   )r  r  r  s     r?   r  z+_prompt_model_selection.<locals>.<listcomp>  s*    999#%s%%999r>   z  Enter custom model namez  Skip (keep current)rM  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.modelsr  r  r  r   anyrt  r  r   simple_term_menur  r  rN  rC  showhermes_cli.curses_uir  rH  r   r.  NotImplementedErrorr}  
subprocessSubprocessErrorr(   	enumerater   r   rJ  rI  )(r  rr  r}  r  r  r  _unavailabler  r  
all_modelspr  r  
cache_readr2  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(    ``                              @@@@@@@r?   _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   r  N)r   r  r  r   r  r=   )r  r  r  rq  s       r?   _save_model_choicer  7  sz     ;:::::::[]]F&**W%%t,, 0%-w	""$h/wKr>   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   )rC  rK  )r   s    r?   login_commandr  H  s;    	
8999	
4555	
RSSS
Q--r>   )force_new_loginr  c                  ~ ~|s)	 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|s"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.rG   r,   r   z6Existing Codex credentials found in Hermes auth store.z!Use existing credentials? [Y/n]: y)r,   r  rB  rB   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  rB  r  rM  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)rg  re  r   )display_hermes_homerO  z
/auth.json)r  r  r   r(   r[  rC  rH  r   r   rI  rJ  rt  r  r%  r  rj  r   r   rN  _codex_device_code_loginhermes_constantsr  )r   r   r  existing_resolved_keyr  rp  
cli_tokens	do_importr  r  _dhhs               r?   _login_openai_codexr  P  s    	g  	8::H
 %LLB77M--- Y- YHghuwyHzHz YNOOO !"EFFLLNNTTVVEE "34      EEE ,,,"=nhll[eg}N~N~""KGGG-...Z{ZZZ[[[F - WXXX 	 	 	D	  -//
 	NOOOcddd !"ghhnnppvvxx		/0      			 L((":...9%<bAAGGIIPPQTUUoYo9.(SSUVVVTUUUV;VVVWWW 
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: rB   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  )rs  r  z$Device auth polling returned status device_code_poll_error
Login cancelled.   z!Login timed out after 15 minutes.device_code_timeoutr.  r  z/deviceauth/callbackzADevice auth response missing authorization_code or code_verifier.device_code_incomplete_exchange)rw  r(  r  r/   r  rs  re  r  zToken exchange failed: token_exchange_failedzToken exchange returned status token_exchange_errorr   rq  z.Token exchange did not return an access_token.token_exchange_no_access_tokenr  rM  )r   rq  r:  r8  rh  zdevice-code)rg  r  re  ri  r   )rs  ry  r	  rw  rv  r
  r   r%  r  r  r  rt  r   rC  	monotonicr  rJ  rK  rx  r   r   r   rN  r  r   r  r   r  r  r>  )_timeissuerr/   rz  r  r   device_datar  r  r  max_waitr  	code_resp	poll_respr.  r  r  
token_resprg  r   rq  r  s                         r?   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#r5   c                 V   ddl } |                     d          dd         }t          j        t	          j        |                                                                                                                    	                    d          }|                     d          }|||fS )zGGenerate (code_verifier, code_challenge_S256, state) for MiniMax OAuth.r   Nr  `   rQ  r  )
secretstoken_urlsaferT  r  r  r  r  r  rV  rN  )r  verifier	challenger   s       r?   _minimax_pkce_pairr  -  s    NNN$$R(("-H(x(())0022 fhhvvc{{  !!"%%EY%%r>   c          
        |                      | dd|t          |d|dddt          t          j                              d          }|j        d	k    r!t          d
|j        p|j         dd          |	                                }dD ]}||vrt          d| dd          |
                    d          |k    rt          ddd          |S )Nz/oauth/coder(  r  )r  r/   r0   r  r  r   rs  r   )r   ru  zx-request-idr  r  z$MiniMax OAuth authorization failed: rn   authorization_failedr`  )r  r  
expired_inz&MiniMax OAuth response missing field: authorization_incompleter   z-MiniMax OAuth state mismatch (possible CSRF).state_mismatch)r
  MINIMAX_OAUTH_SCOPEr(   r  r  r  r%  r@  reason_phraser  r  )rz  r-   r/   r  r   r  rO  r   s           r?   _minimax_request_user_coder   8  s@    {{'''#"(,%+
 
 @(
--
 
   H  s""\8=3ZHDZ\\$+A
 
 
 	
 mmooG@  @@@(/I     
 {{7u$$;$+;
 
 
 	
 Nr>   r  r  interval_msOptional[int]c                  dd l }t          |                                 dz            }||dz  k    r|dz  }	n%|                                 t          d|          z   }	t          d|pddz            }
|                                 |	k     r=|                     | dt          |||d	d
dd          }	 |j        r|                                ni n# t          $ r i Y nw xY w|j        dk    rI	                    di           pi 	                    d          p|j        }t          d|pd dd          	                    d          }|dk    rt          ddd          |dk    r/t          fddD                       st          ddd          S |                    |
           |                                 |	k     =t          ddd           )!Nr   rl  r  g     @@r   g       @i  /oauth/token)rw  r/   r  r  rs  r   rt  r  r  	base_resp
status_msgzMiniMax OAuth error: rD  rn   r  r`  r2  r4  z8MiniMax OAuth reported an error. Please try again later.authorization_deniedsuccessc              3  B   K   | ]}                     |          V  d S r.  r  )r  krO  s     r?   r  z&_minimax_poll_token.<locals>.<genexpr>  s-      __!w{{1~~______r>   )r   rq  r  z<MiniMax OAuth success payload missing required token fields.token_incompletez7MiniMax OAuth timed out before authorization completed.r   )rs  r   rt  r
  MINIMAX_OAUTH_GRANT_TYPEr@  r  r   r  r  r%  allr  )rz  r-   r/   r  r  r  r  r  now_msr  r  r  r1  r2  rO  s                 @r?   _minimax_poll_tokenr  `  sl    $%%FFaK& ::<<#a"4"443,677H
**,,
!
!;;,,,6&&!.	  !D,   
 
	)1>hmmoooBGG 	 	 	GGG	 3&&;;{B//52::<HHYHMC:(8y::(/F   
 X&&WJ(/E    Y____/^_____ R,3E    NHQ **,,
!
!T A y   s   5C C"!C"c                    t                      5  t                      }t          |d|            t          |           ddd           dS # 1 swxY w Y   dS )zGPersist MiniMax OAuth state to Hermes auth store (~/.hermes/auth.json).rn   N)r  r  r  r  )r  r  s     r?   _minimax_save_auth_stater    s    			 % %%''
Z*EEE$$$% % % % % % % % % % % % % % % % % %s   /AAArq   rR  r`  rq   rR  c                   t           d         }| dk    r|j        d         }|j        d         }n|j        }|j        }t	                      \  }}}t                      rd}t          d|  d           t          d|            t          j        t          j	        |          d	d
i          5 }	t          |	||j        ||          }
t          |
d                   }t          |
d                   }t                       t          d           t          d|            t          d|            |r3t          j        |          rt          d           nt          d           |
                    d          }|t!          |          nd}t          d           t#          |	||j        ||t!          |
d                   |          }ddd           n# 1 swxY w Y   t%          j        t(          j                  }t!          |d                   }|                                |z   }d| |||j        t.          |                    dd          |d         |d         |                    d          |                                t%          j        |t(          j                                                  |d}t5          |           t          d            |                    d!          x}rt          d"|            |S )#z?Run MiniMax OAuth flow, persist tokens, return auth state dict.rn   r   rr   rs   Fz#Starting Hermes login via MiniMax (z
) OAuth...Portal: ru  r   rl  )r-   r/   r  r   r  r  To continue:  1. Open:   2. If prompted, enter code: #  (Opened browser for verification)z<  Could not open browser automatically -- use the URL above.r  NzWaiting for approval...r  )r-   r/   r  r  r  r  r~  r  r   rq  r  r$  )r'  rq   r-   r.   r/   r0   r~  r   rq  r  r(  r)  rH  u#   ✓ MiniMax OAuth login successful.notification_messagezNote from MiniMax: )r   r3   r-   r.   r  rF  rC  r	  rw  rv  r   r/   r(   rG  rr  r  r   r  r   r  r   r  r?  r  r  r*  r  )rq   rR  r`  r   r-   r.   r  r  r   rz  	code_dataverification_urlr  interval_rawr  
token_datar  expires_in_sr)  r  r1  s                        r?   _minimax_oauth_loginr    si   
  0G~~!-(<=$]+BC!1$7!3!5!5Hi 	
B
B
B
BCCC	
&_
&
&'''	emO<<');<
> 
> 
> 
AG.O'$E
 
 
	
 y);<==	+.//	n.,..///:y::;;; 	V/00 V;<<<<TUUU }}Z00+7+Cc,''''((((O'x9\233#
 
 

1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
@ ,x|
$
$Cz,/00L</J $*0&$ nn\8<<">2#O4"~66}},ZHLIIISSUU" J  Z(((	
2333nn3444s +)C))***s   .DGG G)r`  forcer   c          	     0   |                      d          st          dddd          	 t          j        |                      dd                                                    }n# t
          $ r d	}Y nw xY wt          j                    }|s||z
  t          k    r| S | d
         }t          j	        t          j
        |                    5 }|                    | dd| d         | d         dddd          }ddd           n# 1 swxY w Y   |j        dk    rV|j                                        t          fddD                       }t          d|j        p|j         dd|          |                                }	|	                     d          dk    rt          dddd          t          j        t&          j                  }
t+          |	d                   }t-          |           }|                    |	d         |	                     d| d                   |
                                t          j        |
                                |z   t&          j                                                  |d           t5          |           |S ) zBRefresh MiniMax OAuth access token if close to expiry (or forced).rq  z:MiniMax OAuth state has no refresh_token; please re-login.rn   no_refresh_tokenTr&  r)  r,   r>  r-   r  r  r/   r  rs  r   rt  r  Nr  c              3      K   | ]}|v V  	d S r.  r4   )r  r  r  s     r?   r  z/_refresh_minimax_oauth_state.<locals>.<genexpr>  s?       Z ZAa4i Z Z Z Z Z Zr>   )ro  rr  invalid_refresh_tokenzMiniMax OAuth refresh failed: refresh_failedr2  r  z-MiniMax OAuth refresh did not return success.r  r   r$  )r   rq  r(  r)  rH  )r  r%  r   r=  r?  r   rs  "MINIMAX_OAUTH_REFRESH_SKEW_SECONDSr	  rw  rv  r
  r  r@  r   r  r  r  r  r   r  r   r=   rM  r  r*  r  )r   r`  r   r)  r  r-   rz  r  r  rO  now_dtr  	new_stater  s                @r?   _refresh_minimax_oauth_stater)    s4   
 99_%% 
H$+=PT
 
 
 	
+EIIlB,G,GHHRRTT

   



)++C j3&*LLL-.O	emO<<	=	=	= 
;;,,,-";/!&!7  !D,   
 

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 s""}""$$ Z Z Z ZXZ Z Z Z ZVX]-Th>TVV$+;$
 
 
 	

 mmooG{{8	));$+;!
 
 
 	

 \(,''Fw|,--LUI/ _eO6LMM'')),V-=-=-?-?,-N08> > >>Gikk"     Y'''s#   :A& &A54A5/DDD)min_token_ttl_secondsr*  c                    t          d          }|r|                    d          st          dddd          t          |          }d|d         |d                             d          d	d
S )z?Return {provider, api_key, base_url, source} for minimax-oauth.rn   r   zMNot logged into MiniMax OAuth. Run `hermes model` and select MiniMax (OAuth).not_logged_inTr&  r.   rM  oauthre  )r  r  r%  r)  rN  )r*  r   s     r?   )resolve_minimax_oauth_runtime_credentialsr.  7  s     $O44E 
		.11 
$?T
 
 
 	

 )//E#(./66s;;	  r>   c                    t          d          } | r|                     d          sdddS 	 t          j        |                     dd                                                    }|t          j                    z
  dk    }n2# t          $ r% t          |                     d                    }Y nw xY w|d|                     dd	          |                     d          d
S )z3Return auth status dict for MiniMax OAuth provider.rn   r   Frb  r)  r,   r   rq   rp   )r%  r'  rq   r)  )r  r  r   r=  r?  rs  r   r   )r   r)  token_valids      r?   get_minimax_oauth_auth_statusr1  K  s    #O44E A		.11 A"@@@6+EIIlB,G,GHHRRTT
!DIKK/14 6 6 6599^44556 !#))Hh//ii--	  s   AB ,B10B1c                   t          | dd          pd}t          | dd           }t          | dd          pd}	 t          |||           dS # t          $ r0}t          t	          |                     t          d	          d}~ww xY w)
z"CLI entry for MiniMax OAuth login.rq   Nrp   rN  Fr   r   r  r   )r   r  r%  rC  r9  rK  )r   r   rq   rR  r   r   s         r?   _login_minimax_oauthr3  ]  s    T8T**6hFt\5999LdIt,,4Gg	
 	
 	
 	
 	
 	
    $$%%%mms   A 
B+BB,  	r-   r.   r/   r0   rR  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.r@   r  r  rM  r  FTzStarting Hermes login via z...r  z'TLS verification: disabled (--insecure)z$TLS verification: custom CA bundle ()ru  r   r  )rz  r-   r/   r0   r  r  rH  r  r  r  r  r  u=     Could not open browser automatically — use the URL above.r   z$Waiting for approval (polling every zs)...r  )rz  r-   r/   r  rH  r  Nr   r.   z%Using portal-provided inference URL: r-   r/   r0   r~  r  r   rq  r(  r)  r$  r  r  r  r  r  r  r  r	  r  r6  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-   rN  r.   r/   r0   r	  rv  rF  rC  r*   rw  r  r(   r   rG  rr  rt  r  r  r  r   r  r   r  rK  r  r?  rO  r  r*  r   r  r%  r(  r  rK  )r-   r.   r/   r0   rR  r`  r  r  r   r   requested_inference_urlr   r  rz  r  r  r  rH  r  rU  effective_intervalr  r  token_expires_inr)  resolved_inference_urlr  r   r  s                                r?   _nous_device_code_loginr<  k  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}  |            }t                       g }|r( |d          } |            }|r |||d          \  }}|	                    dd          }|r4t          d t+          |           d!           t-          ||||"          }nP|r?|pt.                              d#          }t          d$           t          d%| d&           nt          d'           nj# t2          $ r]}t          |t                    rt5          |          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 t9          d||+          }|r!t;          |           t          d,|            t          d-| d.           dS # t<          $ r t          d/           t?          d0          t2          $ r&}t          d1|            t?          d2          d}~ww xY w)3z&Nous Portal device authorization flow.r   Nr   r  Fr  r  r  r  inference_urlr/   r0   rN  r4  r5  r.   r  r@   r  rO  r  r   z,No runtime API key available to fetch modelsrp  r`  r   )get_curated_nous_model_idsget_pricing_for_providercheck_nous_free_tierpartition_nous_models_by_tierT)	free_tierr-   r,   zShowing u=    curated models — use "Enter custom model name" for others.)r}  r  r  rM  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.)rl  zDefault model set to: r  z (model.provider=nous)r  r  zLogin failed: r   ) r   r   r   r   r<  r/   r0   r  r  r  r  r  rC  r   r(   r%  r  r?  r@  rA  rB  r   r  r  rN  r   r9  r  rt  r  rJ  rK  )r   r   r`  r  r  r  r.   _prior_storeprior_active_providerr  rX  selected_modelruntime_keyr?  r@  rA  rB  r  r  r}  rC  _portal_urlr   r*  rp  s                             r?   _login_nousrJ    sZ   dIt44<OGD*e4455Hk4(( 	&9'((	&9_%% s,#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#(               3244IGGG') 226::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P: 4$D$P: $D((P: +D(,P: =/E8,P: 8E<<P: ?E< 4P: 5D?K5 4P: 5
M?AMP: MP: /<N7+P: 7N;;P: >N;?/P: 0AP: :1R+!RRc                $   t          | dd          }|r0t          |          s!t          d|            t          d          t	                      }|p|pt                      }|st          d           dS t          |          }t          |          }t          |          s|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  rC  rK  r  rz  rx  r  r  r|  r   r   )r   r   r  r  config_matchesr  s         r?   logout_commandrM  n  s@   $
D11K 1+>> 0;00111mm ""FLFL&J&L&LF 3444-f55N26::M6"" ;n ;   /}///0009)** 	O=>>>>>MNNNNN9999:::::r>   )r   r(   )rG   r(   r   r(   r   r(   r   r(   )r   r   r   r   r   r   )r   r(   r   r'   r   r   )r   )rG   r(   r   r   r   r   )r4  r   r   r(   )r   r   r   r+  )r   r   )rH  r(   rG  r+  rI  r   r   r,  )r   r   )r`  r   r.  )r  r  r   r2   )r  r2   r   r   )r  r2   r   r(   r   r  )r  r2   r   r(   r   r2   r   r,  )
r  r2   r   r(   r   r2   r  r   r   r,  )r   r(   r   r   )r   r(   r   r(   )r   r+  r   r2   )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   r6  )rC  r   rD  r   r   r   )rH  r   r   r   )r   r   r   r+  )r   r   r   r2   )r   r   rD  r   r   r   )r   r2   )rg  r2   r   r   )rj  r   rD  r   r   r   )ro  )rg  r2   r`  r   r   r2   )r  r   r  r   r  r   r   r2   )r  r   r   r2   )r  r+  r   r  )r  r+  r   r(   )NN)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   r(   r  r(   r  r(   r   r(   )r  r(   r   r  )r  r(   r   r  )r  r(   r`  r   r   r  )r!  r2   r/   r(   r  r(   r"  r(   r  r(   r  r(   r   r  r   r2   )r/   r(   r(  r(   r  r(   r  r(   r  r(   r`  r   r   r2   )r   r2   r`  r   r   r2   )r@  r(   r   r(   )r^  r   r   r2   )rg  r$   re  r(   r   r,  )r   r(   rq  r(   r`  r   r   r2   )rg  r$   r`  r   r   r$   )r   r   )r   r  )r  r  r  r+  r  r  r   r  )
rz  r  r-   r(   r/   r(   r0   r+  r   r2   )rz  r  r-   r(   r/   r(   r  r(   rH  r   r  r   r   r2   )
rz  r  r-   r(   r/   r(   rq  r(   r   r2   )
rz  r  r-   r(   r   r(   r  r   r   r2   )
r.   r(   rG   r(   r`  r   r  r  r   r  )r   r2   r  r   r   r   )
r`  r   r  r  r  r+  r  r   r   r(   )$r   r(   rq  r(   r/   r(   r-   r(   r.   r(   r~  r(   r0   r(   r(  r+  r)  r+  r  r+  r  r+  r   r   r`  r   r  r  r  r+  r  r   r  r   r   r2   )r   r2   r   r   r`  r   r  r   r  r   r   r2   )r  r2   r  r+  )r   r   r`  r   r  r  r  r+  r  r   r   r2   )r   r(   r   r2   )r   r(   r.   r(   rl  r+  r   r   )r,   NNr,   )r  r  rr  r(   r}  r~  r  r  r  r(   r   r+  )r  r(   r   r,  )r   r'   r  r   r   r,  )r   r5   )rz  r  r-   r(   r/   r(   r  r(   r   r(   r   r2   )rz  r  r-   r(   r/   r(   r  r(   r  r(   r  r   r  r  r   r2   )r  r2   r   r,  )rq   r(   rR  r   r`  r   r   r2   )r   r2   r`  r   r   r   r   r2   )r*  r   r   r2   )r   r'   r   r,  )r-   r+  r.   r+  r/   r+  r0   r+  rR  r   r`  r   r  r   r  r+  r   r   r   r2   )r;   
__future__r   r  loggingr   r  r]  r  ro  r  rT  r  r  r  rs  r  rG  
contextlibr   dataclassesr   r   r   r   http.serverr	   r
   pathlibr   typingr   r   r   r   urllib.parser   r   r   r	  yamlr   r   r   r   r  r   utilsr   r   r   	getLoggerr8   r   rl  r   rm  r  r  r  r
  r  r  !DEFAULT_AGENT_KEY_MIN_TTL_SECONDSr  r  r  MINIMAX_OAUTH_CLIENT_IDr  r  MINIMAX_OAUTH_GLOBAL_BASEMINIMAX_OAUTH_CN_BASEMINIMAX_OAUTH_GLOBAL_INFERENCEMINIMAX_OAUTH_CN_INFERENCEr&  r  DEFAULT_GITHUB_MODELS_BASE_URLDEFAULT_COPILOT_ACP_BASE_URLDEFAULT_OLLAMA_CLOUD_BASE_URLSTEPFUN_STEP_PLAN_INTL_BASE_URLSTEPFUN_STEP_PLAN_CN_BASE_URLry  rx  'CODEX_ACCESS_TOKEN_REFRESH_SKEW_SECONDSr  r  &QWEN_ACCESS_TOKEN_REFRESH_SKEW_SECONDSr  r  r  rD  rE  )SPOTIFY_ACCESS_TOKEN_REFRESH_SKEW_SECONDSr  r  r%   r<   r  .GEMINI_OAUTH_ACCESS_TOKEN_REFRESH_SKEW_SECONDSrf  r'   r   r   r   r   r   r   r   r  r  r#  rX  r%  r9  r=  rE  rP  r\  r_  localri  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r5  rB  rG  rK  rO  rX  r[  r]  rf  ri  rn  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r+  r2  r8  r<  r?  rL  rY  rF  rf  rj  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r7  r:  rE  rI  rL  rS  r`  r+  rg  rk  rt  rv  rx  rz  r|  r  r  r  r  r  r  r   r  r  r  r)  r.  r1  r3  r<  rJ  rM  r4   r>   r?   <module>ri     sy     # " " " " "   				   



  



                 % % % % % % ( ( ( ( ( ( ( ( ' ' ' ' ' ' ' ' : : : : : : : :       , , , , , , , , , , , , 6 6 6 6 6 6 6 6 6 6   O O O O O O O O O O 0 0 0 0 0 0 D D D D D D D D D D		8	$	$LLLL   EEEMMMM   FFF     < H % / $+ !$' !() %@ @ 9 G 4 2 !C A %' "3 !@ .  7 "G  F 6 = *- '9 A ), &$B !; H [ A ,/ ) "    y*     
 %< !13 . 1          L0
NN%/5(   L0 NN"1	  L0  .."0	  !L0, $"<	  -L08 5(&  9L0H ~~9M/  IL0X >>!$7/  YL0f nnM=*  gL0v 
>>9G'  wL0F >> 8@(
 
 
GL0\ nn&7-  ]L0j ~~ :-+  kL0z ^^8-)  {L0J 
>>;)'  KL0Z ~~=-+  [L0j ^^0!19)!!9N(BD D
 
 
kL0@ 6\-  AL0 L0P ~~(S/-  QL0` >> *JM7  aL0p ..?0.  qL0@ 8.,  AL0P 
>>0)'  QL0` nn@,*  aL0p .. <0.  qL0@ NN720  AL0P >>
 ;1/  QL0h <.,  iL0x >>=&&  yL0H nn:,*  IL0X @.,  YL0h NN8,*  iL0x ~~L+  yL0H ^^31  IL0 L0  L L L Lf   @ 3    "    89 	 	 	 	 	 	& & & &l 4gY(S	<wi7S;>d>d>df|}CEkEkEk  nC  D# # # # #L. . . .j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%< %< %< %< %<P! ! ! !H< < < <0 0 0 0 4 4 4 4 4 4 S S S S
? ? ? ?	P 	P 	P 	P 	P	, 	, 	, 	,% % % %      ,9 9 9 9- - - -0 0 0 0f% % % % %P	% 	% 	% 	%"   4  $v '+'+	v v v v v vz   "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   6	 	 	 	 	4 4 4 4 4
 #&*    0 #&*( ( ( ( ((( ( ( ( (- - - - -! ! ! ! !
H H H H
4 4 4 4*1 1 1 10#+ #+ #+ #+R #$ $ $ $ $ $^ 04! ! ! ! ! !V ", , , , , ,d "= = = = = =D   $ I	- - - - - -`   &: : : :zZ) Z) Z) Z)@A A A A )- . . . . . .b% % % % %$ "	e e e e e eP   *   H   $ G	* * * * * *b   (  $#+/	           N   6.E .E .E .Ej6W 6W 6W 6Wr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!     > (   2 2 2 2 2 2n  A!##v v v v v vz   -) -) -) -)`2( 2( 2( 2(j.
 .
 .
 .
b   >   <         :* * * *Z% % % %^ $(? ? ? ? ?D   $A A A A      * 37.2m m m m m`   "    "	GL GL GL GL GL GLTN N N Nf& & & &% % % %P= = = =@% % % % D!M M M M M Mb 8<< < < < < <@ %G     (   $     &*(,#!#%@ @ @ @ @ @F} } } }@; ; ; ; ; ;s$   3B8 8CCC CC