
    Fij5                        U d Z ddlmZ ddlZddlZddlmZ ddlmZ ddl	m
Z
 dZ e            Zded	<   i Zd
ed<    e            Zded<   d+dZd,dZd-dZd.d/dZd,dZd0dZd1d Zddd!d2d&Zd3d(Zd4d*ZdS )5zFHelpers for loading Hermes .env files consistently across entrypoints.    )annotationsN)Path)load_dotenv)atomic_replace)_API_KEY_TOKEN_SECRET_KEYzset[str]_WARNED_KEYSzdict[str, str]_SECRET_SOURCES_APPLIED_HOMESenv_varstrreturn
str | Nonec                6    t                               |           S )a  Return the label of the secret source that supplied ``env_var``, if any.

    Returns ``"bitwarden"`` for keys pulled from Bitwarden Secrets Manager
    during the current process's ``load_hermes_dotenv()`` call.  Returns
    ``None`` for keys that came from ``.env``, the shell environment, or
    aren't tracked.  The returned label is metadata only: credential-pool
    persistence may store it to explain the origin of a borrowed secret, but
    must never treat it as authorization to persist the raw value.
    )r   get)r   s    :/home/ubuntu/.hermes/hermes-agent/hermes_cli/env_loader.pyget_secret_sourcer   *   s     w'''    Nonec                 8    t                                            dS )u  Forget which HERMES_HOME paths have already had external secrets applied.

    The first call to ``_apply_external_secret_sources(home_path)`` in a
    process pulls from Bitwarden (or other configured backend), records the
    applied keys in ``_SECRET_SOURCES``, and remembers ``home_path`` so
    subsequent calls in the same process are no-ops.  Call this to force the
    next call to re-pull — useful for tests, and for long-running processes
    that want to refresh after a config change.
    N)r   clear r   r   reset_secret_source_cacher   7   s     r   c                D    t          |           }|sdS |dk    rdS d| dS )u:  Return a human-readable suffix like ``" (from Bitwarden)"`` or ``""``.

    Use this when printing a detected credential so the user can see where
    it came from.  Empty string when the credential came from ``.env`` or
    the shell — those are the implicit / "default" cases users already
    understand.
     	bitwardenz (from Bitwarden)z (from ))r   )r   sources     r   format_secret_source_suffixr!   D   sB     w''F r"" Vr      valuelimitintc                   g }| D ]s}t          |          dk    r^dt          |          d}|                                r	|d|dz  }||vr|                    |           t          |          |k    r ntd                    |          S )zEReturn a compact 'U+XXXX ('c'), ...' summary of non-ASCII codepoints.   zU+04X (r   , )ordisprintableappendlenjoin)r#   r$   seenchlabels        r   _format_offending_charsr3   W   s    D  r77S==&R&&&E~~ &b%D  E"""4yyE!!99T??r   c                    t          t          j                                                  D ]&\  } t	          fdt
          D                       s'	 |                     d           ># t          $ r Y nw xY w|                     dd                              d          }|t          j        <   t          v rt          
                               t          |           t          |          z
  }t          |           pd}t          d d| d|d	k    rd
nd d| d	t          j                   t          dt          j                   (dS )a!  Strip non-ASCII characters from credential env vars in os.environ.

    Called after dotenv loads so the rest of the codebase never sees
    non-ASCII API keys.  Only touches env vars whose names end with
    known credential suffixes (``_API_KEY``, ``_TOKEN``, etc.).

    Emits a one-line warning to stderr when characters are stripped.
    Silent stripping would mask copy-paste corruption (Unicode lookalike
    glyphs from PDFs / rich-text editors, ZWSP from web pages) as opaque
    provider-side "invalid API key" errors (see #6843).
    c              3  B   K   | ]}                     |          V  d S )N)endswith).0suffixkeys     r   	<genexpr>z/_sanitize_loaded_credentials.<locals>.<genexpr>s   s/      KKF3<<''KKKKKKr   asciiignore)errorsznon-printablez  Warning: z contained z non-ASCII character   sr   r)   u8   ) — stripped so the key can be sent as an HTTP header.filea@    This usually means the key was copy-pasted from a PDF, rich-text editor, or web page that substituted lookalike
  Unicode glyphs for ASCII letters. If authentication fails (e.g. "API key not valid"), re-copy the key from the
  provider's dashboard and run `hermes setup` (or edit the .env file in a plain-text editor).N)listosenvironitemsany_CREDENTIAL_SUFFIXESencodeUnicodeEncodeErrordecoder   addr.   r3   printsysstderr)r#   cleanedstrippeddetailr9   s       @r   _sanitize_loaded_credentialsrR   f   s    2:++--.. 
 

UKKKK6JKKKKK 		LL!!!! 	 	 	D	,,wx,88??HH!
3,u::G,(//B?2# 2 2( 2 2!mmss2 2/52 2 2 		
 	
 	
 	
 	1 	
 	
 	
 	
 	
+
 
s   A++
A87A8pathr   overrideboolc                   	 t          | |d           n"# t          $ r t          | |d           Y nw xY wt                       d S )Nutf-8)dotenv_pathrT   encodingzlatin-1)r   UnicodeDecodeErrorrR   )rS   rT   s     r   _load_dotenv_with_fallbackr[      sn    Mx'JJJJJ M M Mx)LLLLLLM !"""""s    44c                ,   |                                  sdS 	 ddlm} n# t          $ r Y dS w xY wddd}	 t	          | fi |5 }|                                }ddd           n# 1 swxY w Y   d |D             } ||          }||k    rddl}|                    t          | j	                  dd	
          \  }}		 t          j        |dd          5 }|                    |           |                                 t          j        |                                           ddd           n# 1 swxY w Y   t!          |	|            dS # t"          $ r( 	 t          j        |	           n# t&          $ r Y nw xY w w xY wdS # t(          $ r Y dS w xY w)u  Pre-sanitize a .env file before python-dotenv reads it.

    python-dotenv does not handle corrupted lines where multiple
    KEY=VALUE pairs are concatenated on a single line (missing newline).
    This produces mangled values — e.g. a bot token duplicated 8×
    (see #8908).

    Also strips embedded null bytes which crash ``os.environ[k] = v``
    with ``ValueError: embedded null byte`` — typically introduced by
    copy-pasting API keys from terminals or rich-text editors.

    We delegate to ``hermes_cli.config._sanitize_env_lines`` which
    already knows all valid Hermes env-var names and can split
    concatenated lines correctly.
    Nr   )_sanitize_env_linesz	utf-8-sigreplace)rY   r=   c                :    g | ]}|                     d d          S ) r   )r^   )r7   lines     r   
<listcomp>z0_sanitize_env_file_if_needed.<locals>.<listcomp>   s&    BBBDLL,,BBBr   z.tmpz.env_)dirr8   prefixwrW   rY   )existshermes_cli.configr]   ImportErroropen	readlinestempfilemkstempr   parentrC   fdopen
writelinesflushfsyncfilenor   BaseExceptionunlinkOSError	Exception)
rS   r]   read_kwforiginalrP   	sanitizedrl   fdtmps
             r   _sanitize_env_file_if_neededr~      s     ;;== 9999999    ')<<G$""'"" 	%a{{}}H	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	%
 CBBBB''11	  OOO&&$$VG '  GBYr3999 )QLL+++GGIIIHQXXZZ((() ) ) ) ) ) ) ) ) ) ) ) ) ) ) sD)))))    IcNNNN   D ! "    s    
--F A$F $A((F +A(,AF >E AD1%E 1D55E 8D59E 
F E.-F .
E;8F :E;;F  F 
FF)hermes_homeproject_envr   str | os.PathLike | Noner   
list[Path]c                F   g }t          | p(t          j        dt          j                    dz                      }|dz  }|rt          |          nd}|                                rt          |           |r#|                                rt          |           |                                r&t          |d           |                    |           |r;|                                r't          ||            |                    |           t          |           |S )a[  Load Hermes environment files with user config taking precedence.

    Behavior:
    - `~/.hermes/.env` overrides stale shell-exported values when present.
    - project `.env` acts as a dev fallback and only fills missing values when
      the user env exists.
    - if no user env exists, the project `.env` also overrides stale shell vars.
    HERMES_HOMEz.hermesz.envNT)rT   )	r   rC   getenvhomerg   r~   r[   r-   _apply_external_secret_sources)r   r   loaded	home_pathuser_envproject_env_paths         r   load_hermes_dotenvr      s0    F[UBImTY[[9=T$U$UVVI6!H,7AtK(((T  /$X... 7,3355 7$%5666  "8d;;;;h (,3355 ("#3&jIIII&'''"9---Mr   r   c                
   t          t          |                                                     }|t          v rdS t                              |           	 t          |           }n# t          $ r Y dS w xY w|pi                     d          pi }|                    d          sdS 	 ddlm	} n# t          $ r Y dS w xY w |d|                    dd          |                    d	d
          t          |                    dd                    t          |                    dd                    t          |                    dd                    t          |                    dd
          pd
                                          |           }|j        rt                       |j        D ]}dt           |<   t#          dt%          |j                   dt%          |j                  dk    rdnd
 dd                    t)          |j                             dt*          j                   |j        r#t#          d|j         t*          j                   |j        D ] }t#          d| t*          j                   !dS )uE  Pull secrets from external sources (currently Bitwarden) into env.

    Runs AFTER dotenv loads so .env values are visible (we use them to
    locate the access token) but BEFORE the rest of Hermes reads
    ``os.environ`` for credentials.  Any failure here is logged and
    swallowed — external secret sources must never block startup.

    Idempotent within a process: subsequent calls for the same
    ``home_path`` are no-ops.  ``load_hermes_dotenv()`` runs at import
    time from several hot modules (cli.py, hermes_cli/main.py,
    run_agent.py, trajectory_compressor.py, ...), so without this guard
    the Bitwarden status line would print 3-5x per CLI startup.  Use
    ``reset_secret_source_cache()`` if you need to force a re-pull
    (tests, future ``hermes secrets bitwarden sync`` from a long-running
    process).
    Nr   enabledr   )apply_bitwarden_secretsTaccess_token_envBWS_ACCESS_TOKEN
project_idr   override_existingFcache_ttl_secondsi,  auto_install
server_url)r   r   r   r   r   r   r   r   z%  Bitwarden Secrets Manager: applied z secretr>   r?   r)   r*   r   r@   z  Bitwarden Secrets Manager: )r   r   resolver   rK   _load_secrets_configrw   r   agent.secret_sources.bitwardenr   ri   rU   floatstripappliedrR   r   rL   r.   r/   sortedrM   rN   errorwarnings)r   home_keycfgbw_cfgr   resultnamewarns           r   r   r      s   " 4	??**,,--H>!!x   "9--    iR__[))/RF::i   JJJJJJJ    %$$68JKK::lB//vzz*=uEEFF

+> D DEE&**^T::;;vzz,339r::@@BB	 	 	F ~ 
 	%&&&
 N 	0 	0D$/OD!!5C4G4G 5 5//144SS"5 5		&00115 5 5 		
 	
 	
 	
 | 
:FL::	
 	
 	
 	
  
 
2D22	
 	
 	
 	
 	

 
s$   A% %
A32A3'B. .
B<;B<dictc                @   | dz  }|                                 si S 	 ddl}n# t          $ r i cY S w xY w	 t          |dd          5 }|                    |          pi }ddd           n# 1 swxY w Y   n# t
          $ r i cY S w xY w|                    d          pi S )zRead just the ``secrets:`` section out of config.yaml.

    Imported lazily and isolated from the main config loader so a
    malformed config can't take down dotenv loading entirely.
    zconfig.yamlr   NrrW   rf   secrets)rg   yamlri   rj   	safe_loadrw   r   )r   config_pathr   ry   datas        r   r   r   F  s    m+K 	   			+sW555 	+>>!$$*D	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+   			88I$"$sA   " 11A7 A+A7 +A//A7 2A/3A7 7BB)r   r   r   r   )r   r   )r   r   r   r   )r"   )r#   r   r$   r%   r   r   )rS   r   rT   rU   r   r   )rS   r   r   r   )r   r   r   r   r   r   )r   r   r   r   )r   r   r   r   )__doc__
__future__r   rC   rM   pathlibr   dotenvr   utilsr   rG   setr   __annotations__r   r   r   r   r!   r3   rR   r[   r~   r   r   r   r   r   r   <module>r      s   L L L " " " " " " 				 



                         A 
      #% $ $ $ $ 355        
( 
( 
( 
(
 
 
 
   &    )
 )
 )
 )
X
# 
# 
# 
#2 2 2 2n -1,0# # # # # #LI
 I
 I
 I
X% % % % % %r   