
    epj\8                       U d Z ddlmZ ddlZddlZddl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 dd	lmZmZmZmZmZmZ d
Zded<    edd          Zd3dZd4dZd5dZdZ dZ! G d de"e          Z#e G d d                      Z$ G d de
          Z% ej&        d           Z' ej&        d!          Z(d6d%Z)d7d'Z*d(de!d)d8d2Z+dS )9um  Secret-source contract: the ABC every secret backend implements.

A *secret source* resolves credentials from an external secret manager
(Bitwarden Secrets Manager, 1Password, an OS keystore, a user script, ...)
into environment-variable-shaped values at process startup, AFTER
``~/.hermes/.env`` has loaded and BEFORE the rest of Hermes reads
``os.environ``.

Scope of the contract (deliberate, please do not widen):

* **Read-only.**  Sources resolve refs → values.  There is no write-back
  ("save this key to your vault"), no arbitrary secret objects, and no
  mid-session secret API.  If a future need for rotation/refresh appears
  it will arrive as a versioned optional hook — do not bolt it on.
* **Startup-time, synchronous.**  ``fetch()`` is called once per process
  (per HERMES_HOME) by the orchestrator in
  :mod:`agent.secret_sources.registry`, which enforces a wall-clock
  timeout around it.  Sources must not spawn background refreshers.
* **Never raises, never prompts.**  ``fetch()`` returns a
  :class:`FetchResult` — errors go in ``result.error`` with a
  machine-readable :class:`ErrorKind`.  Interactive auth belongs in the
  source's CLI ``setup`` flow, never on the startup path (non-TTY
  gateway/cron startup must never block on stdin).
* **Sources fetch; the orchestrator applies.**  A source returns the
  name→value mapping it *would* contribute.  Precedence (mapped-beats-bulk,
  first-wins, ``override_existing``, protected vars), conflict warnings,
  provenance tracking, and the actual ``os.environ`` writes are owned by
  the orchestrator so no backend can get them wrong.

Versioning: ``SECRET_SOURCE_API_VERSION`` gates plugin compatibility.
New *optional* hooks with default implementations do not bump it;
required-signature changes do, and the registry skips (with a warning)
sources built against a different major version instead of crashing
startup.
    )annotationsN)
ContextVarToken)ABCabstractmethod)	dataclassfield)Enum)Path)Dict	FrozenSetListMutableMappingOptionalSequence   z.ContextVar[Optional[MutableMapping[str, str]]]_SOURCE_ENVIRONMENT hermes_secret_source_environment)defaultenvironMutableMapping[str, str]returnr   c                6    t                               |           S )zEInstall a per-fetch environment view without changing ``os.environ``.)r   setr   s    ?/home/thesage/.hermes/hermes-agent/agent/secret_sources/base.pyset_source_environmentr   :   s    ""7+++    tokenNonec                :    t                               |            d S N)r   reset)r   s    r   reset_source_environmentr$   ?   s    e$$$$$r   c                 T    t                                           } | | nt          j        S )zDReturn the active per-fetch environment, or the process environment.)r   getosr   r   s    r   get_source_environmentr(   C   s$    !%%''G)77rz9r   g      ^@g      >@c                  6    e Zd ZdZdZdZdZdZdZdZ	dZ
d	Zd
ZdS )	ErrorKinda[  Machine-readable failure taxonomy for :class:`FetchResult.error`.

    A fixed vocabulary keeps startup warnings and ``hermes secrets status``
    uniform across backends, and lets the orchestrator implement
    kind-dependent policy (e.g. a future stale-cache fallback on
    ``NETWORK``/``TIMEOUT`` but not on ``AUTH_FAILED``) exactly once.
    not_configuredbinary_missingauth_failedauth_expiredref_invalidnetworkempty_valuetimeoutinternalN)__name__
__module____qualname____doc__NOT_CONFIGUREDBINARY_MISSINGAUTH_FAILEDAUTH_EXPIREDREF_INVALIDNETWORKEMPTY_VALUETIMEOUTINTERNAL r   r   r*   r*   Q   sG          &N%NK!LKGKGHHHr   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e          Zded<   d	Zd
ed<   d	Zded<   d	Zded<   edd            Zd	S )FetchResultag  Outcome of one source's fetch.

    ``secrets`` holds what the source *would* contribute; whether each
    var is actually applied is the orchestrator's decision.  ``applied``
    and ``skipped`` exist for backward compatibility with the original
    Bitwarden fetch-and-apply entry point and are left empty by
    conforming ``fetch()`` implementations.
    )default_factoryzDict[str, str]secretsz	List[str]appliedskippedwarningsNOptional[str]errorzOptional[ErrorKind]
error_kindzOptional[Path]binary_pathr   boolc                    | j         d u S r"   )rJ   selfs    r   okzFetchResult.okz   s    zT!!r   )r   rM   )r4   r5   r6   r7   r	   dictrE   __annotations__listrF   rG   rH   rJ   rK   rL   propertyrQ   rA   r   r   rC   rC   e   s           $eD999G9999t444G4444t444G4444%555H5555E&*J**** #'K&&&&" " " X" " "r   rC   c                      e Zd ZU dZeZded<   dZded<   dZded<   dZ	ded	<   d
Z
ded<   edd            Zd dZd dZd!dZd"dZd#dZd$dZd
S )%SecretSourceu[  One external secret backend.

    Subclasses set the class attributes and implement :meth:`fetch`.
    Everything else has a sensible default.

    Attributes:
        name: Config-section key under ``secrets:`` in config.yaml.
            Lowercase ``[a-z0-9_]+``.  Also the provenance label stored
            for every var this source supplies.
        label: Human-readable name used in startup messages and
            ``hermes secrets status`` (e.g. ``"Bitwarden Secrets Manager"``).
        shape: ``"mapped"`` when the user explicitly binds env-var names
            to refs (1Password ``env:`` map, command source) or
            ``"bulk"`` when the backend injects whole projects/folders
            of secrets implicitly (Bitwarden BSM).  The orchestrator
            gives mapped sources precedence over bulk sources: an
            explicit binding is stronger intent than a project dump.
        scheme: Optional URI scheme this source owns for secret
            references (``"op"`` for ``op://...``).  Must be unique
            across registered sources — refs may eventually appear
            outside the ``secrets:`` block (e.g. credential-pool
            ``api_key`` fields), so scheme collisions are rejected at
            registration time to keep that future possible.
        api_version: Contract version this source was built against.
    intapi_version strnamelabelmappedshapeNrI   schemecfgrR   	home_pathr   r   rC   c                    dS )u  Resolve this source's secrets. MUST NOT raise or prompt.

        ``cfg`` is the source's raw config section (``secrets.<name>``)
        from config.yaml — treat every field defensively, the section
        may be malformed.  ``home_path`` is the resolved HERMES_HOME.
        NrA   )rP   ra   rb   s      r   fetchzSecretSource.fetch   s      r   rM   c                p    t          t          |t                    o|                    d                    S )z'Whether the user turned this source on.enabledrM   
isinstancerR   r&   rP   ra   s     r   
is_enabledzSecretSource.is_enabled   s+    JsD))@cggi.@.@AAAr   c                r    t          t          |t                    o|                    dd                    S )u  May this source overwrite vars that .env / the shell already set?

        This NEVER extends to vars claimed by another secret source in the
        same startup pass — cross-source overrides are a config error the
        orchestrator warns about, not a knob.
        override_existingFrg   ri   s     r   rl   zSecretSource.override_existing   s0     JsD))Qcgg6I5.Q.QRRRr   FrozenSet[str]c                    t                      S )a  Env vars the orchestrator must never let ANY source overwrite.

        Typically the source's own bootstrap-auth var (e.g.
        ``BWS_ACCESS_TOKEN``) so a vault that contains its own access
        token can't clobber the credential used to reach it.
        )	frozensetri   s     r   protected_env_varszSecretSource.protected_env_vars   s     {{r   floatc                    	 t          |pi                     dt                              }n# t          t          f$ r
 t          cY S w xY w|dk    r|nt          S )z;Wall-clock budget the orchestrator enforces around fetch().timeout_secondsr   )rq   r&   DEFAULT_FETCH_TIMEOUT_SECONDS	TypeError
ValueError)rP   ra   vals      r   fetch_timeout_secondsz"SecretSource.fetch_timeout_seconds   si    	1(9;XYYZZCC:& 	1 	1 	10000	1Aggss#@@s   *- AAc                    i S )zOptional description of this source's config keys.

        Shape: ``{key: {"description": str, "default": Any}}``.  Used by
        setup surfaces to render config without hardcoding per-source
        knowledge.  Purely informational.
        rA   rO   s    r   config_schemazSecretSource.config_schema   s	     	r   kindOptional['ErrorKind']c                $   t           j        d| j         dt           j        d| j         dt           j        d| j         dt           j        d| j         dt           j        dt           j        d| j         d	i}||                    |d          ndS )u@  One-line, actionable next step for a failed fetch.

        Called by the startup status printer (and ``hermes secrets ...
        status``) right after a fetch error is surfaced, so the user sees
        *what to run* next to fix it — not just what broke.  Sources
        should override this to point at their own CLI verbs (e.g.
        ``hermes secrets bitwarden token`` for AUTH_FAILED).  Return an
        empty string to suppress the hint.

        Must never raise and must not perform I/O — it's a pure
        kind→string mapping on the startup path.
        zRun `hermes secrets z  setup` to finish configuration.z" setup` to install the helper CLI.u-   Credentials rejected — run `hermes secrets z setup` to re-authenticate.u,   Credentials expired — run `hermes secrets uN   Network problem reaching the secrets backend — check connectivity and retry.u#   Backend was slow — raise secrets.z .timeout_seconds if this recurs.NrZ   )	r*   r8   r\   r9   r:   r;   r=   r?   r&   )rP   r{   ra   generics       r   remediationzSecretSource.remediation   s     $RtyRRR$TtyTTT!&	 & & & "&ty & & & * "di " " ")
0 )-(8w{{4$$$b@r   )ra   rR   rb   r   r   rC   )ra   rR   r   rM   )ra   rR   r   rm   )ra   rR   r   rq   )r   rR   )r{   r|   ra   rR   r   r[   )r4   r5   r6   r7   SECRET_SOURCE_API_VERSIONrY   rS   r\   r]   r_   r`   r   rd   rj   rl   rp   rx   rz   r   rA   r   r   rW   rW      s         4 1K0000DNNNNEOOOOE F        ^B B B BS S S S   A A A A   %A %A %A %A %A %Ar   rW   z^[A-Za-z_][A-Za-z0-9_]*$z<\x1b(?:\[[0-9;?]*[ -/]*[@-~]|\][^\x07\x1b]*(?:\x07|\x1b\\)?)r\   r[   rM   c                n    t          |           o&t          t                              |                     S )z8True when ``name`` is a legal environment-variable name.)rM   _ENV_NAME_REmatch)r\   s    r   is_valid_env_namer     s)    ::8$|11$77888r   textc                <    t                               d| pd          S )zDStrip ANSI escape sequences (whole CSI/OSC sequences, not just ESC).rZ   )_ANSI_REsub)r   s    r   
scrub_ansir     s    <<DJB'''r   rA   )	allow_env	extra_envr2   argvSequence[str]r   r   Optional[Dict[str, str]]r2   rq   subprocess.CompletedProcessc          
        d}i }g ||R D ](}t           j                            |          }||||<   )|r|                    |           |                    dd           	 t          j        t          |           |dddd|t
          j                  }n# t
          j	        $ rA}	t          t          t          | d	                             j         d
|dd          |	d}	~	wt          $ r@}	t          dt          t          | d	                             j         d|	           |	d}	~	ww xY w|j        pd|_        t!          |j        pd          |_        |S )u  Run a secret-manager helper CLI with a minimal, allowlisted env.

    Security posture shared by every subprocess-driven backend:

    * argv list only — never ``shell=True``.  Callers pass user-supplied
      reference strings AFTER a ``--`` option terminator in their argv.
    * The child gets ``PATH``/``HOME``/locale basics plus only the env
      vars named in ``allow_env`` (auth/session vars) and ``extra_env``
      — never a copy of the full post-dotenv ``os.environ``, which by
      this point holds every credential Hermes knows about.
    * ``NO_COLOR=1`` is set and stderr/stdout are ANSI-scrubbed so
      helper diagnostics can't smuggle escape sequences into Hermes
      output.
    * stdin is ``/dev/null`` so a helper that decides to prompt fails
      fast instead of hanging startup.

    Raises ``RuntimeError`` on spawn failure or timeout (message safe to
    surface); returns the completed process otherwise — callers own
    returncode interpretation.
    )
PATHHOMEUSERPROFILE
SYSTEMROOTTMPDIRTEMPLANGLC_ALLXDG_CONFIG_HOMEXDG_DATA_HOMENNO_COLOR1Tzutf-8replace)envcapture_outputr   encodingerrorsr2   stdinr   z timed out after z.0fszfailed to invoke z: rZ   )r'   r   r&   update
setdefault
subprocessrunrT   DEVNULLTimeoutExpiredRuntimeErrorr   r[   r\   OSErrorstdoutr   stderr)
r   r   r   r2   	base_keepr   keyrw   procexcs
             r   run_secret_clir     s   6GIC''Y''  jnnS!!?CH 

9NN:s###~JJ	$
 
 
 $   CQLL!!&GGGGGG
 
	    @Sa\\ 2 2 7@@3@@
 
	
 +#DKT[.B//DKKs$   #3B D/&<C""D//;D**D/)r   r   r   r   )r   r   r   r    )r   r   )r\   r[   r   rM   )r   r[   r   r[   )
r   r   r   r   r   r   r2   rq   r   r   ),r7   
__future__r   r'   rer   contextvarsr   r   abcr   r   dataclassesr   r	   enumr
   pathlibr   typingr   r   r   r   r   r   r   rS   r   r   r$   r(   rt   DEFAULT_CLI_TIMEOUT_SECONDSr[   r*   rC   rW   compiler   r   r   r   r   rA   r   r   <module>r      sw  " " "H # " " " " " 				 				     ) ) ) ) ) ) ) ) # # # # # # # # ( ( ( ( ( ( ( (             L L L L L L L L L L L L L L L L
   C C C C j!CTRRR , , , ,
% % % %: : : : !&  #     T   ( " " " " " " " "2zA zA zA zA zA3 zA zA zAD rz566 2:UVV9 9 9 9
( ( ( (  "*.0: : : : : : : :r   