
    `gj,                        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mZm	Z	m
Z
 dZ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Zddd	 	 	 	 	 ddZy)z0Shared helpers for direct xAI HTTP integrations.    )annotationsN)AnyDictOptionali ' i  c                    t         j                  j                  dd      j                         ry	 ddlm}   |        dz  }|j                         syt        j                  |j                               }t        |t              r|j                  d      nd	}t        |t              r|j                  d
      nd	}t        |t              r|j                  d      nd	}t        |t              r|j                  d      nd	}t        |xs d      j                         ryt        |t              r|j                  d      nd	}t        |t              r|j                  d
      nd	}t        |t              rG|D ]B  }	t        |	t              st        |	j                  dd      xs d      j                         sB y y# t        $ r Y yw xY w)u  Cheap probe — return True when xAI credentials are *likely* usable.

    Deliberately avoids :func:`resolve_xai_http_credentials` so callers in
    hot-paint paths (``hermes tools`` repaint, tool-registration scans,
    ``WebSearchProvider.is_available()``) don't incur disk locks or — in
    the OAuth path — a network token refresh. The ABC contract on
    :meth:`agent.web_search_provider.WebSearchProvider.is_available`
    explicitly forbids network calls for exactly this reason.

    Resolution order, fast-to-slow:

    1. ``XAI_API_KEY`` env var (cheapest; covers explicit-key users).
    2. ``~/.hermes/auth.json`` has a non-empty ``providers.xai-oauth.tokens.access_token``
       (single file read, no expiry check, no refresh).
    3. ``credential_pool.xai-oauth`` has any entry with a non-empty
       ``access_token`` (covers multi-account ``hermes auth add xai-oauth``
       grants that are pool-only / ``manual:device_code``).

    Returns False on any exception so a corrupted auth store can't block
    other availability scans. Truthful refresh + expiry handling happens
    in ``search()`` (or whichever caller actually makes the request).
    XAI_API_KEY Tr   get_hermes_homez	auth.jsonF	providersN	xai-oauthtokensaccess_tokencredential_pool)osenvirongetstriphermes_constantsr   existsjsonloads	read_text
isinstancedictstrlist	Exception)
r   	auth_pathstorer   	xai_stater   r   r   entriesentrys
             A/root/.hermes/venv/lib/python3.12/site-packages/tools/xai_http.pyhas_xai_credentialsr%      s   . 
zz~~mR(..04#%3	!

9..01.8.EEIIk*4	2<Y2MIMM+.SW	,6y$,Gx(T5?5Mvzz.1SW|!r"((* ;EUD:Q%))$56W[ /40 , 	
 gt$   !%.uyy4:;AAC	 
  s+    F= CF= BF= 9F= ;F= =	G	G	c                    	 ddl m}  ||       }||S 	 t        j                  j                  | |      S # t        $ r Y +w xY w)zRead ``name`` from ``~/.hermes/.env`` first, then ``os.environ``.

    Wraps :func:`hermes_cli.config.get_env_value` so tests can patch
    ``tools.xai_http.get_env_value`` to inject dotenv-only secrets into the
    xAI credential resolver.
    r   )get_env_value)hermes_cli.configr'   r   r   r   r   )namedefault_hermes_get_env_valuevalues       r$   r'   r'   I   sO    L%d+L  ::>>$((  s   5 	A Ac                 <    	 ddl m}  d|  S # t        $ r d} Y w xY w)z>Return a stable Hermes-specific User-Agent for xAI HTTP calls.r   __version__unknownzHermes-Agent/)
hermes_clir/   r   r.   s    r$   hermes_xai_user_agentr2   [   s/     * ;-((    s    c                    	 ddl m}  |       }t        |t              r|j	                  |       nd}t        |t              r|S i S # t
        $ r i cY S w xY w)z=Return a top-level Hermes config section as a dict, or empty.r   )load_configN)r(   r4   r   r   r   r   )section_namer4   cfgsections       r$   _load_config_sectionr8   d   sR    1m+5c4+@#'','d$Wd3w;; 	s   AA A AAc                    t        | t              r| S t        | t              r(| j                         j	                         }|dv ry|dv ry|S )N>   1onyestrueenabledT>   0noofffalsedisabledF)r   boolr   r   lower)r,   r*   
normalizeds      r$   _coerce_boolrG   p   sL    %%[[]((*
>>@@N    c                4   | yt        | t              r4| j                         j                         }|dv ry|dv ry	 t	        |      } t        | t        t        f      r!t	        |       }|dk  ryt        |t              S t        S # t
        $ r	 t        cY S w xY w)zNormalize an xAI storage TTL.

    Returns:
        int seconds for an expiring file,
        None for permanent storage (omit expires_after on the wire).
    N>   r	   r*   >   r?   nonenullneverforever	permanentr   )
r   r   r   rE   int
ValueError&SAFE_XAI_STORAGE_EXPIRES_AFTER_SECONDSfloatmin%MAX_XAI_STORAGE_EXPIRES_AFTER_SECONDS)r,   rF   secondss      r$   _coerce_expires_afterrV   |   s     }%[[]((*
(OO	:
OE %#u&e*a<7ABB11  	:99	:s   B BBc                x   t        |       }t        |t              r|j                  d      nd}t        |t              r|j                  d      nd}t        |t              r|ni }t	        |j                  d      d      }t	        |j                  d      d      }t        |j                  d            }|||dS )	a  Read storage settings for xAI Imagine under image_gen/video_gen config.

    Supported config shape:

        image_gen:
          xai:
            storage:
              enabled: true
              public_url: true
              expires_after: null     # omit for permanent public URLs

    The same shape is accepted under ``video_gen.xai.storage``. Storage is on
    by default so xAI returns permanent public URLs instead of short-lived CDN URLs.
    xaiNstorager>   T
public_urlexpires_after)r>   rZ   r[   )r8   r   r   r   rG   rV   )r5   r7   xai_sectionrY   r>   rZ   r[   s          r$   read_xai_imagine_storage_configr]      s     #<0G(27D(A'++e$tK,6{D,Ikooi(tG#GT2gG7;;y148Ggkk,7>J)'++o*FGM  & rH   c               b   t        |       }|d   syt        j                  j                  t        j                        }|j	                  d      }t        j                         j                  dd }|j                  d      xs d}| d| d| d| t        |d         d	}|d
   |d
   |d
<   |S )zAReturn an xAI ``storage_options`` payload, or None when disabled.r>   Nz%Y%m%d-%H%M%S   .bin-rZ   )filenamerZ   r[   )
r]   datetimenowUTCstrftimeuuiduuid4hexlstriprD   )	r5   filename_prefix	extensionr6   re   tsshortextpayloads	            r$   build_xai_storage_optionsrr      s     *,
7Cy>





-C	o	&BJJLRa E


3

(5C&'qAeWAcU;3|,-G ?'#&#7 NrH   c                v    t        |       }|d   sy|d   d}n|d   dz  }d|dd|d	k7  rd
nd }d| d|  dS )z5User-facing notice for first xAI Imagine storage use.r>   r	   r[   zwithout an automatic expiryiQ z
for about gz day   szMxAI Imagine storage is enabled so generated media gets a reusable public URL zK. xAI may bill for stored files and public URL hosting. Disable this with `zL.xai.storage.enabled: false` or set `expires_after` to change the retention.)r]   )r5   r6   	retentiondayss       r$   xai_storage_notice_textry      su    
),
7Cy>
?#1	?#|4 aDAIS2,FG		[ !''3n 5:	:rH   c                X   t        |       }|sy	 ddlm}  |       dz  }|j                  dd       ||  dz  }|j	                         ry|j                  t        j                  j                  t        j                        j                         dz          |S # t        $ r |cY S w xY w)	zBReturn the storage notice once per Hermes home, then mark it seen.Nr   r
   stateT)parentsexist_ok_xai_storage_notice_seen
)ry   r   r   mkdirr   
write_textrd   re   rf   	isoformatr   )r5   noticer   
marker_dirmarkers        r$   "maybe_mark_xai_storage_notice_seenr      s    $\2F4$&0
5.FGG==?(++//=GGIDPQ s   ;B AB B)(B)F)force_refreshapi_key_hintc                <   	 ddl m} ddlm}  |d      }| r|j	                  |      n|j                         }| r||j                         }t        t        |dd      xs t        |dd            j                         }t        t        |dd      xs t        |d	d      xs |j                        j                         j                  d
      }t        t        d      xs t        d      xs d      j                         j                  d
      }|j                  ||      }	|rd||	dS 	 t        t        d      xs d      j                         }
t        t        d      xs d      j                         j                  d
      }	d|
|	dS # t        $ r Y lw xY w)u;  Resolve bearer credentials for direct xAI HTTP endpoints.

    Prefers Hermes-managed xAI OAuth credentials when available, then falls back
    to ``XAI_API_KEY`` resolved via ``hermes_cli.config.get_env_value`` so keys
    stored in ``~/.hermes/.env`` (the standard Hermes location) are honored —
    not just ones already exported into ``os.environ``. This keeps direct xAI
    endpoints (images, TTS, STT, etc.) aligned with the main runtime auth model
    and preserves the regression contract from PR #17140 / #17163.

    Set ``force_refresh=True`` to perform an unconditional OAuth refresh.
    Reactive callers should also pass the rejected bearer as ``api_key_hint``
    so a freshly loaded multi-account pool refreshes the exact issuing entry,
    not whichever entry its strategy would otherwise select first.
    r   )	load_poolNr   runtime_api_keyr   r	   runtime_base_urlbase_url/HERMES_XAI_BASE_URLXAI_BASE_URL)fallback)providerapi_keyr   r   zhttps://api.x.ai/v1rX   )agent.credential_poolr   hermes_cli.authauthtry_refresh_matchingselectr   getattrr   DEFAULT_XAI_OAUTH_BASE_URLrstripr'    _xai_validate_inference_base_urlr   )r   r   r   auth_modpoolr#   r   fallback_base_urloverride_base_urlr   r   s              r$   resolve_xai_http_credentialsr      s   &(3*%  %%l3 	
 U] KKMEE,d3 2unb1
 %' 	  E-t4 3uj"-322
 %'&&+	 	
  /0 ^,
 %'&&+	 	
 <<& = 
 ''$   -.4"5;;=G=0I4IJPPRYYZ]^H   s   D*F 	FF)returnrD   )N)r)   r   )r   r   )r5   r   r   zDict[str, Any])r,   r   r*   rD   r   rD   )r,   r   r   zOptional[int])r5   r   rl   r   rm   r   r   zOptional[Dict[str, Any]])r5   r   r   r   )r5   r   r   Optional[str])r   rD   r   r   r   zDict[str, str])__doc__
__future__r   rd   r   r   rh   typingr   r   r   rT   rQ   r%   r'   r2   r8   rG   rV   r]   rr   ry   r   r    rH   r$   <module>r      s    6 "   	  & & ): %)9 &6r)$)		26>  	
 0$*  "&CC  C 	CrH   