
    NQj~5                    n   U d Z ddlmZ ddlZddlZddlmZmZ ddl	m
Z
 ddlmZmZmZ ddlmZmZmZmZmZ  ej        e          Zi Zded	<   d
ae G d d                      Ze G d d                      Ze G d d                      Zd
dd,dZd-dZd.dZ d/dZ!d/d Z"d0d&Z#d1d(Z$	 d2d3d+Z%dS )4u  Secret-source registry + apply orchestrator.

This module owns everything that must be uniform across secret backends
so no individual source can get it wrong:

* registration (name/scheme uniqueness, API-version gating)
* per-source wall-clock timeout enforcement around ``fetch()``
* precedence: mapped sources beat bulk sources; within a shape,
  ``secrets.sources`` order (or registration order) decides; first
  claim wins — later sources never silently clobber an earlier one
* ``override_existing`` semantics (may beat .env/shell, never another
  secret source, never a protected var)
* cross-source conflict warnings (shadowed claims are always surfaced)
* provenance: which source supplied every applied var

The single entry point for startup is :func:`apply_all`, called from
``hermes_cli.env_loader._apply_external_secret_sources()``.

Plugins register additional sources via
``PluginContext.register_secret_source()`` which lands in
:func:`register_source`.  In-tree sources are registered lazily by
:func:`_ensure_builtin_sources` — the set of bundled sources is
deliberately closed (Bitwarden, and 1Password once it lands); new
third-party backends ship as standalone plugin repos implementing
:class:`agent.secret_sources.base.SecretSource`.
    )annotationsN)	dataclassfield)Path)DictListOptional)SECRET_SOURCE_API_VERSION	ErrorKindFetchResultSecretSourceis_valid_env_namezDict[str, SecretSource]_SOURCESFc                  <    e Zd ZU dZded<   ded<   ded<   ded<   dS )	
AppliedVarz7Provenance record for one env var the orchestrator set.strnamesourceshapebooloverrode_envN)__name__
__module____qualname____doc____annotations__     A/home/alina/.hermes/hermes-agent/agent/secret_sources/registry.pyr   r   4   s?         AAIIIKKKJJJr   r   c                      e Zd ZU dZded<   ded<   ded<    ee          Zded	<    ee          Zded
<    ee          Z	ded<    ee          Z
ded<    ee          Zded<   dS )SourceReportz4One source's outcome within an :class:`ApplyReport`.r   r   labelr   resultdefault_factory	List[str]appliedskipped_existingskipped_claimedskipped_protectedskipped_invalidN)r   r   r   r   r   r   listr'   r(   r)   r*   r+   r   r   r   r!   r!   >   s         >>IIIJJJt444G4444"'%"="="=====!&t!<!<!<O<<<<#(5#>#>#>>>>>!&t!<!<!<O<<<<<<r   r!   c                      e Zd ZU dZ ee          Zded<    ee          Z	ded<    ee          Z
ded<   edd            ZdS )ApplyReportz.Merged outcome of one orchestrated apply pass.r$   zList[SourceReport]sourceszDict[str, AppliedVar]
provenancer&   	conflictsreturnr   c                *    t          | j                  S N)r   r0   )selfs    r   applied_anyzApplyReport.applied_anyT   s    DO$$$r   N)r2   r   )r   r   r   r   r   r,   r/   r   dictr0   r1   propertyr6   r   r   r   r.   r.   L   s         88"'%"="="=G====(-d(C(C(CJCCCC 5666I6666% % % X% % %r   r.   )replacer   r   r9   r   r2   c          	        t          | t                    st                              d|            dS t	          | dd          pd}|r@|                    dd                                          r||                                k    rt                              d|           dS t	          | dd          t          k    r3t                              d	|t	          | dd
          t                     dS t	          | dd          dvr-t                              d|t	          | dd                     dS |t          v r|st                              d|           dS t	          | dd          }|rZt          
                                D ]@\  }}||k    r5t	          |dd          |k    r t                              d|||            dS A| t          |<   dS )ub  Register a secret source.  Returns True on success.

    Rejections are logged, never raised — a bad plugin must not take
    down startup.  ``replace`` allows tests / user plugins to override
    a bundled source of the same name (last-writer-wins like model
    providers), but scheme collisions across *different* names are
    always rejected.
    z=Ignoring secret source %r: does not inherit from SecretSourceFr    _z+Ignoring secret source with invalid name %rapi_versionNzXIgnoring secret source '%s': built against secret-source API v%s, this Hermes speaks v%s?r   )mappedbulkzEIgnoring secret source '%s': shape must be 'mapped' or 'bulk', got %rz9Secret source '%s' already registered; ignoring duplicateschemezKIgnoring secret source '%s': scheme '%s://' is already owned by source '%s'T)
isinstancer   loggerwarninggetattrr9   isalnumlowerr
   r   items)r   r9   r   rA   
other_nameothers         r   register_sourcerK   ^   s    fl++ K	
 	
 	
 u662&&,"D t||C,,4466 $$**,,:N:NDdKKKuv}d++/HHH%'&-557P	
 	
 	

 uvw%%-???S'&'400	
 	
 	
 uxRTXYYYuVXt,,F !)!1!1 	 	JT!!geXt&D&D&N&N+&*  
 uuHTN4r   r   r   Optional[SecretSource]c                R    t                       t                              |           S r4   )_ensure_builtin_sourcesr   get)r   s    r   
get_sourcerP      s     <<r   List[SecretSource]c                 j    t                       t          t                                                    S r4   )rN   r,   r   valuesr   r   r   list_sourcesrT      s&    !!"""r   Nonec                 H   t           rdS da 	 ddlm}  t           |                        n,# t          $ r t
                              dd           Y nw xY w	 ddlm} t           |                       dS # t          $ r  t
                              dd           Y dS w xY w)	zIdempotently register the bundled sources.

    Lazy so importing this module stays cheap and so a broken bundled
    source can never break registration of the others.
    NTr   )BitwardenSourcez2Failed to register bundled Bitwarden secret sourceexc_info)OnePasswordSourcez2Failed to register bundled 1Password secret source)	_BUILTINS_LOADEDagent.secret_sources.bitwardenrW   rK   	ExceptionrC   rD    agent.secret_sources.onepasswordrZ   )rW   rZ   s     r   rN   rN      s     &BBBBBB))**** & & &K $ 	 	& 	& 	& 	& 	&&&FFFFFF))++,,,,, & & &K $ 	 	& 	& 	& 	& 	& 	&&s!   + &AAA7 7&B! B!c                 <    t                                            dad S )NF)r   clearr[   r   r   r   _reset_registry_for_testsra      s    NNr   cfgr7   	home_pathr   r   c                   |                      |          }t          j                            dd| j                   }	 |                    | j        ||          }	 |                    |          }n# t          j        j        $ rb |	                                 t                      }d|dd| j         d|_        t          j        |_        |cY |                    d	
           S t           $ r`}t                      }dt#          |          j         d| |_        t          j        |_        |cY d}~|                    d	
           S d}~ww xY w	 |                    d	
           n# |                    d	
           w xY wt)          |t                    s>t                      }dt#          |          j         d|_        t          j        |_        |S |S )u  Run source.fetch() under a wall-clock budget; never raises.

    The budget is enforced with a daemon worker thread: a source that
    blows its budget is reported as ``TIMEOUT`` and its (eventual)
    result is discarded.  The thread itself may linger until process
    exit — acceptable for a startup-only path, and strictly better than
    an unbounded hang on every ``hermes`` invocation.
       zsecret-src-)max_workersthread_name_prefix)timeoutzfetch exceeded z.0fuB   s budget — startup continued without this source (raise secrets.z-.timeout_seconds if the backend is just slow)F)waitzfetch raised z: Nzfetch returned z instead of FetchResult)fetch_timeout_seconds
concurrentfuturesThreadPoolExecutorr   submitfetchr#   TimeoutErrorcancelr   errorr   TIMEOUT
error_kindshutdownr]   typer   INTERNALrB   )	r   rb   rc   rh   executorfuturer#   resexcs	            r   _fetch_with_timeoutr|      s    **3//G!44*E*E*E 5  H&sI>>	]]7]33FF!. 		 		 		MMOOO--CN'N N N;N N N I
 '.CNJJ 	u%%%%  	 	 	--CCS		(:CCcCCCI&/CNJJJJJu%%%%	 " 	u%%%%u%%%%fk** mmLd6ll3LLL 		 #+
MsI    E- A4 3E- 4AEE- )	E2?E1E2E- EE- -Fsecrets_cfgc                   t                       |                     d          }g }t          |t                    r|D ]9}t          |t                    r"|t
          v r||vr|                    |           :d |D             }|rIt                              dd	                    |          d	                    t
                    pd           t
          D ]}||vr|                    |           g }|D ]}t
          |         }|                     |          }t          |t                    r|ni }	 |                    |          r|                    |           i# t          $ r  t                              d|d           Y w xY w|S )	aE  Resolve which sources run, in which order.

    Order: the optional ``secrets.sources`` list wins; sources not named
    there follow in registration order.  Enabled = the source's own
    ``is_enabled`` says so for its config section.  Mapped-vs-bulk
    precedence is applied on top of this order by :func:`apply_all`.
    r/   c                N    g | ]"}t          |t                    r|t          v |#S r   )rB   r   r   ).0es     r   
<listcomp>z,_ordered_enabled_sources.<locals>.<listcomp>   sB     @ @ @ C((@-.h->-> ->->->r   z7secrets.sources names unknown source(s): %s (known: %s)z, nonez0Secret source '%s' is_enabled() raised; skippingTrX   )rN   rO   rB   r,   r   r   appendrC   rD   joinr7   
is_enabledr]   )	r}   explicitorderentryunknownr   enabledr   rb   s	            r   _ordered_enabled_sourcesr      s    y))HE(D!! 
 	$ 	$E%%% $%8*;*;U@R@RU###@ @h @ @ @ 	NNI		'""DIIh$7$7$A6     uLL"$G 	0 	0$ood##T**2cc	0  %% 'v&&& 	0 	0 	0NNM$  0 0 0 0 0	0 Ns   3*E'FFenvironOptional[Dict[str, str]]c           
        ddl }||n|j        }t                      }t          | t                    r| ni } t          |           }|s|S d |D             d |D             z   }g }i }	|D ]}
|                     |
j                  }t          |t                    r|ni }t          |
||          }|	                    |
||f           	 |

                    |          D ]}|	                    ||
j                   # t          $ r Y w xY wi }|D ]\  }
}}t          |
j        |
j        p|
j        |          }|j        	                    |           |j        sL	 |
                    |          }n# t          $ r d}Y nw xY w|j                                        D ]W\  }}t          |t(                    rt          |t(                    s1t+          |          s|j        	                    |           [||	v r|j        	                    |           z||v rI|j        	                    |           |j        	                    | d||          d|
j         d	           t5          |                    |                    }|r|s|j        	                    |           	|||<   |
j        ||<   |j        	                    |           t;          ||
j        |
j        |
          |j        |<   Y|S )u]  Fetch from every enabled source and apply the merged result to env.

    ``environ`` defaults to ``os.environ``; injectable for tests.

    Precedence per env var (most-specific intent wins):

    1. Pre-existing env (.env / shell) — unless the winning source has
       ``override_existing: true``.
    2. Mapped sources, in configured order.
    3. Bulk sources, in configured order.

    First claim wins.  A later source that also carries the var gets a
    ``skipped_claimed`` entry and a conflict warning — never a silent
    clobber, and ``override_existing`` never applies across sources.
    r   Nc                (    g | ]}|j         d k    |S )r?   r   r   ss     r   r   zapply_all.<locals>.<listcomp>3  s$    :::aag&9&9&9&9&9r   c                (    g | ]}|j         d k    |S )r@   r   r   s     r   r   zapply_all.<locals>.<listcomp>4  s$    :::6(9(9!(9(9(9r   )r   r"   r#   Fz: kept value from z; uW    also supplies it (first source wins — remove one binding or reorder secrets.sources))r   r   r   r   ) osr   r.   rB   r7   r   rO   r   r|   r   protected_env_vars
setdefaultr]   r!   r"   r/   okoverride_existingsecretsrH   r   r   r+   r*   r)   r1   r   r(   r'   r   r   r0   )r}   rc   r   _osenvreportr   orderedfetches	protectedr   rb   r#   varclaimedsroverridevalueexisteds                      r   	apply_allr     s   " (''ckC]]F!+K!>!>F++BK&{33G  ;:7:::::W:::;G =?G "I 	 	oofk**T**2cc$VS)<<V,---	0055 7 7$$S&+66667 	 	 	D	 !G& * *Vv{ & ;!') ) ) 	b!!!y 		//44HH 	 	 	HHH	 !...00 	 	JCc3'' z%/E/E $S)) "))#...i$++C000g~~"))#... '' E Egcl E E{E E E  
 3773<<((G x #**3///CH!;GCLJc"""%/{l$	& & &Fc""1	> Ms$   3C<<
D	D	E33FF)r   r   r9   r   r2   r   )r   r   r2   rL   )r2   rQ   )r2   rU   )r   r   rb   r7   rc   r   r2   r   )r}   r7   r2   rQ   r4   )r}   r7   rc   r   r   r   r2   r.   )&r   
__future__r   concurrent.futuresrk   loggingdataclassesr   r   pathlibr   typingr   r   r	   agent.secret_sources.baser
   r   r   r   r   	getLoggerr   rC   r   r   r[   r   r!   r.   rK   rP   rT   rN   ra   r|   r   r   r   r   r   <module>r      sb    6 # " " " " "      ( ( ( ( ( ( ( (       ' ' ' ' ' ' ' ' ' '              
	8	$	$ %' & & & &          
= 
= 
= 
= 
= 
= 
= 
= 	% 	% 	% 	% 	% 	% 	% 	%" >C . . . . . .b   
# # # #
& & & &4   , , , ,^& & & &T 37\ \ \ \ \ \ \r   