
    .cj              
         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mZmZ ddlmZ ddl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)  ej*        e+          Z,dZ-de- Z.de- dZ/dZ0dZ1ee2e2e2f         Z3i Z4de5d<   dZ6dZ7dZ8dZ9dpdZ: e#e6e:          Z;de5d<   dqdrd"Z<dqdrd#Z=dsd$Z>d%d&dtd)Z?dud*Z@dud+ZAd%d,dvd.ZBdwd2ZCdxd5ZDdyd7ZEdzd;ZFd{d>ZGd|d@ZHd}dCZId~dEZJddHZKddIddLZLddIddPZMddQdRdSdd%ddTdd]ZNdd^ZO	 ddd`ZPdadSd%dQdRdSdd%ddb	ddhZQ G di dje)          ZRddmZSdqddnZTdqddoZUdS )uB  Bitwarden Secrets Manager (`bws` CLI) integration.

Hermes pulls API keys from Bitwarden Secrets Manager at process startup
so they don't have to live in plaintext in ``~/.hermes/.env``.

Design summary
--------------

* The ``bws`` binary is auto-installed into ``<hermes_home>/bin/bws`` on
  first use.  Hermes pins one version (``_BWS_VERSION``) and downloads
  the matching asset from the official GitHub Releases page, verifying
  the SHA-256 against the release's published checksum file.
* The access token is stored in ``~/.hermes/.env`` as
  ``BWS_ACCESS_TOKEN`` (or whatever name the user picked in
  ``secrets.bitwarden.access_token_env``).  This is the one
  bootstrap secret — every other provider key can live in Bitwarden.
* Pulling secrets is a single ``bws secret list <project_id>
  --output json`` call.  We cache the result in-process for
  ``cache_ttl_seconds`` so back-to-back ``hermes`` invocations don't
  hammer the API.
* Failures NEVER block Hermes startup.  Missing binary, no network,
  expired token, etc. all emit a one-line warning and continue with
  whatever credentials ``.env`` already had.

The module is intentionally subprocess-driven rather than going through
the ``bitwarden-sdk-secrets`` Python package: one cross-platform binary
is easier to lazy-install than a wheels-with-Rust-extension dependency.
    )annotationsN)Path)DictListOptionalTuple)hashes)AESGCM)HKDF)CachedFetch	DiskCacheFetchResultis_valid_env_name)	ErrorKindSecretSourcez2.0.0z;https://github.com/bitwarden/sdk-sm/releases/download/bws-vzbws-sha256-checksums-z.txt<      zDict[_CacheKey, _CachedFetch]_CACHEzbws_cache.jsonzbws_cache.enc.json   s   hermes-bws-encrypted-cache-v1	cache_key	_CacheKeyreturnstrc                "    | \  }}}| d| d| S )z:Serialize a cache key to a stable string for JSON storage.| )r   token_fp
project_id
server_urls       @/home/ice/.hermes/hermes-agent/agent/secret_sources/bitwarden.py_cache_key_strr!   i   s*    '0$Hj*2222j222    )key_serializerr   _DISK_CACHE	home_pathOptional[Path]r   c                6    t                               |           S )zReturn the disk cache path under hermes_home/cache/.

    Thin wrapper over the shared DiskCache, kept for tests and any direct
    callers; falls back to `$HERMES_HOME` / `~/.hermes` when home is None.
    )r$   pathr%   s    r    _disk_cache_pathr*   t   s     I&&&r"   c                :    ddl m}  ||           dz  t          z  S )z>Return the encrypted disk cache path under hermes_home/cache/.r   )resolve_cache_homecache)agent.secret_sources._cacher,   _ENCRYPTED_CACHE_BASENAME)r%   r,   s     r    _encrypted_disk_cache_pathr0   }   s0    >>>>>>i((725NNNr"   c                 (    ddl m}   |             dz  S )z9Where Hermes stores its managed binaries.  Profile-aware.r   get_hermes_homebin)hermes_constantsr3   r2   s    r    _hermes_bin_dirr6      s'    000000?u$$r"   Finstall_if_missingr8   boolc                |   t                      t                      z  }|                                r!t          j        |t          j                  r|S t          j        d          }|rt          |          S | rB	 t                      S # t          $ r&}t                              d|           Y d}~dS d}~ww xY wdS )uR  Return a path to a usable ``bws`` binary, or None.

    Resolution order:
      1. ``<hermes_home>/bin/bws``  (our managed copy — preferred)
      2. ``shutil.which("bws")``    (system PATH)

    When ``install_if_missing`` is True and neither resolves, this calls
    :func:`install_bws` to download and verify the pinned version.
    bwszbws auto-install failed: %sN)r6   _platform_binary_nameexistsosaccessX_OKshutilwhichr   install_bws	Exceptionloggerwarning)r8   managedsystemexcs       r    find_bwsrJ      s     "7"9"99G~~ BIgrw77 \%  F F|| 	==  	 	 	NN8#>>>44444	 4s   ;B	 	
B9B44B9c                 8    t          j                    dk    rdndS )NWindowszbws.exer;   )platformrH   r   r"   r    r<   r<      s     ))Y6699EAr"   c                    t          j                    } t          j                                                    }| dk    rdt           dS | dk    r|dv rdnd}d| d	t           dS | d
k    r|dv rdnd}d}	 t          j        ddgdddt
          j                  }d|j        |j	        z                                   v rd}n# t          t
          j        f$ r Y nw xY wd| d| dt           dS t          d|  d|           )u   Map (uname, arch, libc) → the upstream asset filename.

    Asset names follow Rust's target triple convention.  Linux defaults
    to gnu (glibc); we switch to musl only if ldd --version says so.
    Darwinzbws-macos-universal-z.ziprL   )arm64aarch64rQ   x86_64zbws-z-pc-windows-msvc-Linuxgnulddz	--versionT   )capture_outputtexttimeoutstdinmuslz-unknown-linux--z+Unsupported platform for bws auto-install:  )rM   rH   machinelower_BWS_VERSION
subprocessrunDEVNULLstdoutstderrOSErrorTimeoutExpiredRuntimeError)rH   r^   archlibcress        r    _platform_asset_namerl      sm    _F  &&((G 9l8888#';;;yy?d??\????#';;;yy	.$# (  C #*sz188::::23 	 	 	D	DdDD4DD,DDDD
HfHHwHH  s   7A
C CC)forcerm   c           	     \   t                      }|                    dd           |t                      z  }|                                r| s|S t	                      }t
           d| }t
           dt           }t          j        d          5 }t          |          }||z  }|t          z  }	t                              d|           t          ||           t          ||	           t          |	|          }
t          |          }|
                                |                                k    rt!          d| d|
 d	|           t#          j        |          5 }t'          |t                                }t)          |||          }d
d
d
           n# 1 swxY w Y   t          j        t-          |          d          \  }}t/          j        |           t3          j        ||           t/          j        |t8          j        t8          j        z  t8          j        z  t8          j         z  t8          j!        z  t8          j"        z  t8          j#        z             t/          j$        ||           d
d
d
           n# 1 swxY w Y   t                              dtJ          |           |S )uZ  Download, verify, and install the pinned ``bws`` binary.

    Returns the path to the installed executable.  Raises on any
    failure (network, checksum, extraction) — callers in the auto-install
    path catch these; the user-facing ``hermes secrets bitwarden setup``
    surface lets them propagate so the wizard can show a clear error.
    Tparentsexist_ok/zhermes-bws-)prefixzDownloading %szChecksum mismatch for z: expected z, got Nz.bws_)dirrs   zInstalled bws %s at %s)&r6   mkdirr<   r=   rl   _BWS_RELEASE_BASE_BWS_CHECKSUM_NAMEtempfileTemporaryDirectoryr   rE   info_http_download_expected_sha256_sha256_filer_   rh   zipfileZipFile_pick_zip_member_safe_extract_membermkstempr   r>   closerA   copy2chmodstatS_IRUSRS_IWUSRS_IXUSRS_IRGRPS_IXGRPS_IROTHS_IXOTHreplacer`   )rm   bin_dirtarget
asset_name	asset_urlchecksum_urltmpdirtmpzip_pathchecksum_pathexpectedactualzfmember	extractedfdstageds                    r    rC   rC      s    GMM$M...,...F}} u %''J$33z33I'>>*<>>L		$M	:	:	: $#f6ll#00$i000y(+++|]333#M:>>h''>>v||~~--5 5 5$5 5,25 5  
 _X&& 	>"%b*?*A*ABBF
 -R==I	> 	> 	> 	> 	> 	> 	> 	> 	> 	> 	> 	> 	> 	> 	> %#g,,wGGG
F
Y'''
L4<'$,6l!\*l!\*	
 	
 	
 	
66"""I$# $# $# $# $# $# $# $# $# $# $# $# $# $# $#L KK(,???Ms8   CJ .FJ F	J F	 CJ  JJurldestNonec                   t           j                            | ddi          }	 t           j                            |t                    5 }t          |d          5 }t          j        ||           d d d            n# 1 swxY w Y   d d d            d S # 1 swxY w Y   d S # t           j        j	        $ r}t          d|  d|           |d }~ww xY w)Nz
User-Agentzhermes-agent)headers)rY   wbzFailed to download : )urllibrequestRequesturlopen_BWS_DOWNLOAD_TIMEOUTopenrA   copyfileobjerrorURLErrorrh   )r   r   reqrespfrI   s         r    r{   r{     sd   
.
 
 |^.L
 
M
MCH^##C1F#GG 	,4dD!! ,Q"4+++, , , , , , , , , , , , , , ,	, 	, 	, 	, 	, 	, 	, 	, 	, 	, 	, 	, 	, 	, 	, 	, 	, 	, <  H H H=====>>CGHs_   &B# BA>2B>B	BB	B	B# BB# BB# #C7CCchecksum_filer   c                0   |                      dd          }|                                D ]Q}|                                                                }t	          |          dk    r|d         |k    r
|d         c S Rt          d| d| j                   )	zParse the upstream ``bws-sha256-checksums-X.Y.Z.txt`` file.

    Format is the standard ``sha256sum`` output: ``<hex>  <filename>``,
    one per line.
    utf-8r   )encodingerrorsrV   r   zNo checksum entry for z in )	read_text
splitlinesstripsplitlenrh   name)r   r   rX   linepartss        r    r|   r|   !  s     ""GI"FFD!!  

""$$u::??uRyJ668OOO
EEE1CEE  r"   r(   c                    t          j                    }t          | d          5 t          fdd          D ]}|                    |           	 d d d            n# 1 swxY w Y   |                                S )Nrbc                 .                          d          S )Ni   )read)r   s   r    <lambda>z_sha256_file.<locals>.<lambda>4  s    !&&-- r"   r"   )hashlibsha256r   iterupdate	hexdigest)r(   hchunkr   s      @r    r}   r}   1  s    A	dD		 Q////55 	 	EHHUOOOO	               ;;==s   ,AA"%A"r   zipfile.ZipFilebinary_namec                    fd|                                  D             }|s0t          d d|                                  dd          d          |                    t                     |d         S )	zFind the binary inside the upstream zip.

    Historically the archive has been flat (``bws`` at the root) but we
    tolerate a top-level directory just in case upstream changes.
    c                R    g | ]#}|                     d           d         k    !|$S )rr   r   )r   ).0nr   s     r    
<listcomp>z$_pick_zip_member.<locals>.<listcomp>?  s2    NNNaggcll2.>+.M.M!.M.M.Mr"   zCould not find z% inside downloaded archive (members: N   z...))keyr   )namelistrh   sortr   )r   r   
candidatess    ` r    r   r   9  s     ONNNR[[]]NNNJ 
1k 1 1rr*1 1 1
 
 	

 OOOa=r"   r   dest_dirc                   t           j                            |          }t           j                            t           j                            ||                    }	 t           j                            ||g          |k    }n# t
          $ r d}Y nw xY w|r||k    rt          d|d          |                     ||           t          |          S )aM  Extract a single archive member, refusing path traversal.

    ``ZipFile.extract`` will happily honour member names containing
    ``../`` or absolute paths, letting a malicious archive write outside
    ``dest_dir`` (a "zip-slip").  We resolve the would-be target and
    confirm it stays within ``dest_dir`` before extracting.
    Fz*Refusing to extract unsafe archive member z%: it escapes the extraction directory)	r>   r(   realpathjoin
commonpath
ValueErrorrh   extractr   )r   r   r   	dest_rootr   	containeds         r    r   r   J  s       **IWbgll9f==>>FG&&	6':;;yH		   			 
)++3 3 3 3
 
 	
 JJvy!!!<<s   %B BBtokenc                    t          j        |                     d                                                    dd         S )uE   SHA-256 prefix used as a cache key — never logged, never displayed.r   N   )r   r   encoder   )r   s    r    _token_fingerprintr   j  s3    >%,,w//00::<<SbSAAr"   rawbytesc                P    t          j        |                               d          S )Nascii)base64	b64encodedecode)r   s    r    _b64er   o  s!    C  ''000r"   rX   c                T    t          j        |                     d          d          S )Nr   T)validate)r   	b64decoder   )rX   s    r    _b64dr   s  s$    DKK004@@@@r"   access_tokensaltc                    t          t          j                    d|t                                        |                     d                    S )zCDerive the local cache encryption key from the bootstrap BWS token.    )	algorithmlengthr   rz   r   )r   r	   SHA256_ENCRYPTED_CACHE_INFOderiver   )r   r   s     r    _derive_encrypted_cache_keyr   w  sH    -//"	  
 f\  ))**+r"   r)   entry_CachedFetchc                   t          |          }	 |j        }|                    dd           	 t          j        |d           n# t
          $ r Y nw xY wt          j        d          }t          j        d          }t          |           }t          ||          }	t          j
        |j        |j        dd                              d	          }
t          |	                              ||
|                    d	                    }t           |t#          |          t#          |          t#          |          d
}t%          j        ddt)          |                    \  }}	 t          j        |dd	          5 }t          j        ||           ddd           n# 1 swxY w Y   t          j        |d           t          j        ||           	 t1          |                                           n# t4          $ r Y nt
          $ r Y nw xY wdS dS dS # t6          $ r( 	 t          j        |           n# t
          $ r Y nw xY w w xY w# t8          $ r Y dS w xY w)zPersist an encrypted last-good cache entry atomically.

    Best-effort by design: cache write failure must never block a fresh BWS
    fetch.  The raw BWS access token is not stored; it only derives the AES key.
    Tro   i  r      secrets
fetched_at),:)
separatorsr   )versionr   r   nonce
ciphertextz.bws_cache_enc_z.tmp)rs   suffixrt   wr   Ni  )r0   parentru   r>   r   rf   urandomr!   r   jsondumpsr  r  r   r
   encrypt_ENCRYPTED_CACHE_VERSIONr   rx   r   r   fdopendumpr   r*   unlinkFileNotFoundErrorBaseExceptionrD   )r   r   r   r%   r(   	cache_dirr   r  serialized_keyr   	plaintextr  payloadr   r   r   s                   r    _write_encrypted_disk_cacher    s    &i00D0K	t444	HY&&&& 	 	 	D	z"~~
2'	22),==JU5EFF!
 
 
 &// 	 C[[((9n33G<<
 

 0!$KK5\\
++
 
 "$VY
 
 
C	2sW555 &	'1%%%& & & & & & & & & & & & & & &HS%   JsD!!! ++224444$       54 	 	 		#   	    s   I A I 
AI ADI &H =FH F##H &F#'-H !G7 6H 7
HH 	HH HH 
I
#H87I
8
II
II

I 
IImax_age_secondsfloatOptional[_CachedFetch]c                $   |dk    rdS t          |          }	 t          j        |                    d                    }t	          |t
                    sdS t          |           }|                    d          t          k    rdS |                    d          |k    rdS t          t          |                    dd                              }t          t          |                    d	d                              }t          t          |                    d
d                              }	t          ||          }
t          |
                              ||	|                    d                    }t          j        |                    d                    }t	          |t
                    sdS |                    d          }|                    d          }t	          |t
                    rt	          |t           t"          f          sdS t%          j                    t#          |          z
  }|dk     s||k    rdS d |                                D             }t)          |t#          |                    S # t*          $ r Y dS w xY w)zHReturn a decrypted encrypted-cache entry if it matches and is in-window.r   Nr   r  r  r   r    r  r  r  r  c                n    i | ]2\  }}t          |t                    t          |t                    /||3S r   )
isinstancer   )r   kvs      r    
<dictcomp>z._read_encrypted_disk_cache.<locals>.<dictcomp>  sP     
 
 
Q!S!!
&0C&8&8
q
 
 
r"   r   )r0   r  loadsr   r"  dictr!   getr  r   r   r   r
   decryptr   r   intr  timeitemsr   rD   )r   r   r  r%   r(   r  r  r   r  r  r   r   innerr  inner_fetched_at	entry_agetypeds                    r    _read_encrypted_disk_cacher1    sd    !t%i00D *T^^W^==>>'4(( 	4'	22;;y!!%===4;;u//4SVR001122c'++gr2233443w{{<<<==>>
),==Skk!!:~44W==
 
 
3::g..//%&& 	4))I&& 99\22'4(( 	
;KcSX\0Z0Z 	4IKK%(8"9"99	q==I774
 
$]]__
 
 
 Ee<L6M6MNNNN   tts7   =J -J J "DJ 7AJ /J ;J 
JJ,  Tr   )binarycache_ttl_seconds	use_cacher   r%   encrypted_cache_enabled!encrypted_cache_max_stale_secondsr   r3  r4  r5  r   r6  r7   Tuple[Dict[str, str], List[str]]c        	        \   | st          d          |st          d          t          |           ||pdf}	|r|dk    rt                              |	          }
|
r|
                    |          r	|
j        g fS |rt          |	| ||          }nt                              |	||          }||t          |	<   |j        g fS |pt          d          }|t          d	          	 t          || ||          \  }}nB# t           $ r4}t          t          |                    }|r|t          j        t          j        fv r|rlt          |	| ||          }|Vt!          d
t#          j                    |j        z
            }|t          |	<   |j        d| dt'          |           dgfcY d}~S n|dk    rt                              |	t)          d          |          }|Vt!          d
t#          j                    |j        z
            }|t          |	<   |j        d| dt'          |           dgfcY d}~S  d}~ww xY wt+          |t#          j                              }|rI|dk    r
|t          |	<   |rt-          |	| ||           n#|dk    rt                              |	|||           ||fS )u  Pull the secrets for ``project_id`` from Bitwarden Secrets Manager.

    Returns ``(secrets_dict, warnings_list)``.

    Set ``server_url`` to point at a non-default Bitwarden region or a
    self-hosted instance — e.g. ``https://vault.bitwarden.eu`` for EU
    Cloud accounts.  When empty, ``bws`` uses its built-in default
    (``https://vault.bitwarden.com``, US Cloud).  This is plumbed into
    the subprocess as ``BWS_SERVER_URL``.

    ``cache_ttl_seconds`` controls the normal fresh cache.  When
    ``encrypted_cache_enabled`` is true, fresh cache entries are written as
    AES-GCM encrypted JSON instead of plaintext, and a last-good encrypted
    entry may be used after NETWORK/TIMEOUT failures for up to
    ``encrypted_cache_max_stale_seconds``.  This stale fallback is separate
    from the fresh-cache TTL so operators can set ``cache_ttl_seconds: 0``
    while still keeping an encrypted break-glass cache for offline startup.

    Raises :class:`RuntimeError` for fatal conditions (missing binary,
    auth failure, unparseable output).  Callers in the env_loader path
    catch this and emit a single warning; callers in the user-facing
    setup wizard let it propagate.
    zBitwarden access token is emptyzBitwarden project_id is emptyr   r   )r   r   r  r%   NTr7   u   bws binary not available — auto-install failed and `bws` is not on PATH.  Install manually from https://github.com/bitwarden/sdk-sm/releases or re-run `hermes secrets bitwarden setup`.        zbws live fetch failed (z/); falling back to stale ENCRYPTED disk cache (zs old)infz%); falling back to stale disk cache (r   )r   r   r   r%   )rh   r   r   r(  is_freshr  r1  r$   r   rJ   _run_bws_list_classify_bws_errorr   r   NETWORKTIMEOUTmaxr+  r  r*  r  r   r  write)r   r   r3  r4  r5  r   r%   r6  r7  r   cacheddisk_cachedr;   r  warningsrI   kindstaleager   s                       r    fetch_bitwarden_secretsrI    s   F  ><=== <:;;;#L11:z?ORPI +&**I&& 	&foo&788 	&>2%%" 	T4#) 1#	  KK &**96GSSK" !,F9&**

5H555C
{0
 
 	
0)#|ZTT . . .* #3s88,, 	)"3Y5F!GGG& 2'!-$E'	   $c49;;1A#ABBC(-F9% =H# H H7:3xxH H H+        % #Q&&#((E%LL)LL$c49;;1A#ABBC(-F9% =N# N N=@XXN N N+        	].^ TY[[AAAE Nq   %F9" 	N (#)#	     ""i0A9MMMHs2   $C; ;H:B H5&H:,BH5.H:4H55H:c                x   |                      dd                                          }|s|S g }|                                D ]_}|                                }|                    d          r n3|dv r2t	          j        dd|          }|r|                    |           `|rd                    |          n|S )a  Reduce a bws (Rust color-eyre) error dump to its cause line(s).

    bws failures look like::

        Error:
           0: Received error message from server: [400 Bad Request] {"error":"invalid_client"}

        Location:
           crates/bws/src/main.rs:108
        ...

    Everything from ``Location:`` on is diagnostic noise for a Hermes
    user.  Keep the numbered cause lines (joined), drop the rest, and
    fall back to the stripped raw text when the shape is unrecognized.
    r   )z	Location:zBacktrace omittedz	Run with )r   zError:z^\d+:\s*z; )r   r   r   
startswithresubappendr   )r   rX   causesr   strippeds        r    _summarize_bws_stderrrR  y  s      ;;vr""((**D F!! 	$ 	$::<<NOO 	E~%%6+r844 	$MM(### &0499VD0r"   r;   c                   t          |           dd|ddg}t          j                                        }||d<   |                    dd           |r||d<   	 t          j        ||d	d	t          t
          j        
          }nP# t
          j	        $ r}t          dt           d          |d }~wt          $ r}t          d|           |d }~ww xY w|j        dk    r?t          |j        p|j        pd          }t          d|j         d|d d                    |j                                        }	|	si dgfS 	 t#          j        |	          }
n*# t"          j        $ r}t          d|           |d }~ww xY wt)          |
t*                    s$t          dt-          |
          j                   i }g }|
D ]}t)          |t0                    s|                    d          }|                    d          }t)          |t                     rt)          |t                     smt5          |          s|                    d|d           |||<   ||fS )Nsecretlistz--outputr  BWS_ACCESS_TOKENNO_COLOR1BWS_SERVER_URLT)envrW   rX   rY   rZ   zbws timed out after zs fetching secretszfailed to invoke bws: r   r   zbws exited r      z'bws returned no output (empty project?)zbws returned non-JSON output: zbws returned unexpected shape: r   valuezSkipping secret z: not a valid env-var name)r   r>   environcopy
setdefaultra   rb   _BWS_RUN_TIMEOUTrc   rg   rh   rf   
returncoderR  re   rd   r   r  r&  JSONDecodeErrorr"  rU  type__name__r'  r(  _is_valid_env_namerO  )r;   r   r   r   cmdrZ  procrI   errr   r  r  rE  itemr   r\  s                   r    r=  r=    s    s88Xvz:v
FC
*//

C*CNN:s###  + *D~$$
 
 
 $   G#3GGG
 
	  D D D9C99::CD !
 $DK$D4;$D"EE8$/88S#Y88
 
 	
 +



C ?=>>>L*S// L L LACAABBKL gt$$ 
Fd7mm.DFF
 
 	
 !GH  $%% 	hhuoo!!#s## 	:eS+A+A 	!#&& 	OOD3DDD   Hs<   )B CB))C6C		C<E E8 E33E8rV  )	access_token_envr   override_existingr4  auto_installr   r%   r6  r7  enabledrj  rk  rl  r   c        
   
        t                      }
| s|
S t          j                            |d                                          }|sd| d|
_        |
S |s	d|
_        |
S t          |          }||
_        |	d|
_        |
S 	 t          ||||||||	          \  }}n-# t          $ r }t          |          |
_        |
cY d}~S d}~ww xY w||
_        |
j                            |           |                                D ]\  }}||k    r|
j                            |           &|s:t          j                            |          r|
j                            |           b|t          j        |<   |
j                            |           |
S )	u8  Pull secrets from BSM and set them on ``os.environ``.

    This is the function ``load_hermes_dotenv()`` calls after the .env
    files have loaded.  It is intentionally defensive — any failure
    returns a :class:`FetchResult` with ``error`` set; it never raises.

    ``server_url`` selects the Bitwarden region or self-hosted endpoint
    (e.g. ``https://vault.bitwarden.eu`` for EU Cloud).  Empty string
    means use ``bws``'s default (US Cloud).

    Parameters mirror the ``secrets.bitwarden.*`` config keys so the
    caller can just splat the dict in.
    r   &secrets.bitwarden.enabled is true but 3 is not set.  Run `hermes secrets bitwarden setup`.Msecrets.bitwarden.project_id is empty.  Run `hermes secrets bitwarden setup`.r7   Nhbws binary not available and auto-install is disabled.  Run `hermes secrets bitwarden setup` to install.r   r   r3  r4  r   r%   r6  r7  )r   r>   r]  r(  r   r   rJ   binary_pathrI  rh   r   r  rE  extendr,  skippedrO  applied)rm  rj  r   rk  r4  rl  r   r%   r6  r7  resultr   r3  r  rE  rI   r   r\  s                     r    apply_bitwarden_secretsry    s   4 ]]F :>>"2B77==??L >5E > > > 	  4 	 666FF~? 	 3%!/!$;.O	
 	
 	
    3xx FN
O8$$$mmoo # #
U""" N!!#&&&  	RZ^^C%8%8 	N!!#&&&
3c""""Ms   B 
C'C<CCc                  T     e Zd ZdZdZdZdZdZdd
ZddZ	ddZ
ddZd fdZ xZS )BitwardenSourceu   Bitwarden Secrets Manager as a registered secret source.

    Thin adapter over the module's fetch machinery.  ``fetch()`` only
    *fetches* — precedence, override semantics, conflict warnings, and
    the ``os.environ`` writes are the orchestrator's job
    (see ``agent.secret_sources.registry.apply_all``).

    Bitwarden is a **bulk** source: it injects every secret in the
    configured BSM project, so explicit per-var bindings from mapped
    sources (e.g. the 1Password ``env:`` map) outrank it.
    	bitwardenzBitwarden Secrets Managerbulkr;   cfgr'  r   r9   c                r    t          t          |t                    o|                    dd                    S )Nrk  T)r9   r"  r'  r(  )selfr~  s     r    rk  z!BitwardenSource.override_existingY  s0    
 JsD))Pcgg6I4.P.PQQQr"   c                    d}t          |t                    r$t          |                    d          p|          }t	          |h          S )NrV  rj  )r"  r'  r   r(  	frozenset)r  r~  	token_envs      r    protected_env_varsz"BitwardenSource.protected_env_vars`  sI    &	c4   	FCGG$677D9EEI)%%%r"   c           
     N    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S )NzMaster switchF)descriptiondefaultz0Env var holding the machine-account access tokenrV  zBSM project UUIDr   z9Fresh disk+memory cache TTL; 0 disables fresh-cache reuser2  z6Encrypted last-good cache for network/timeout fallbackr   )rm  max_stale_secondsz&BSM values overwrite .env/shell valuesTz#Auto-download the pinned bws binaryz0Region / self-hosted endpoint (empty = US Cloud))rm  rj  r   r4  encrypted_cacherk  rl  r   r   )r  s    r    config_schemazBitwardenSource.config_schemaf  s    '65IIQ-! ! +=LLZ" "
  X$)*      H" "
  E 
  R 5
 
 	
r"   r%   r   r   c                   t          |t                    r|ni }t                      }t          |                    d          pd          }t
          j                            |d                                          }|sd| d|_        t          j
        |_        |S t          |                    d          pd          }|sd|_        t          j
        |_        |S t          |                    dd	                    }t          |
          }||_        |d|_        t          j        |_        |S 	 t!          |                    dd                    }	n# t"          t$          f$ r d}	Y nw xY w|                    d          }
t          |
t                    r|
ni }
t          |
                    dd                    }	 t!          |
                    dd                    }n# t"          t$          f$ r d}Y nw xY w	 t'          ||||	t          |                    dd          pd                                          |||          \  }}nv# t(          $ ri}t          |          |_        t+          t          |                    |_        |j        t          j        k    rd| d|j         d|_        |cY d }~S d }~ww xY w||_        |j                            |           |S )Nrj  rV  r   ro  rp  r   rq  rl  Tr7   rr  r4  r2  g     r@r  rm  Fr  r   r:  r   rs  z5Bitwarden rejected the machine-account access token (uF   ) — it was likely revoked, expired, or belongs to another region.  ())r"  r'  r   r   r(  r>   r]  r   r   r   NOT_CONFIGURED
error_kindr9   rJ   rt  BINARY_MISSINGr  	TypeErrorr   rI  rh   r>  AUTH_FAILEDr  rE  ru  )r  r~  r%   rx  rj  r   r   rl  r3  ttlencrypted_cfgencrypted_enabledencrypted_max_staler  rE  rI   s                   r    fetchzBitwardenSource.fetch  sB   T**2ccsww'9::P>PQQz~~&6;;AACC 	B9I B B B L !* 8FM..4"55
 	8 L !* 8FMCGGND99::\:::#>C L !* 8FM	 3S99::CC:& 	 	 	CCC	  122)3M4)H)HPb !2!29e!D!DEE	&"'(9(9:Mq(Q(Q"R"R:& 	& 	& 	&"%	&	 7)%"%sww|R88>B??EEGG#(92E	! 	! 	!GXX  	 	 	s88FL 3CHH = =F I$999G(G G7=|G G G 
 MMMMMM	 !x(((sD   5#E E/.E/#G( (G>=G>AI 
KAK :K Kr   c                    |t           j        t           j        fv r	 dS t                                          ||          S )Nu   Run `hermes secrets bitwarden token` to paste a fresh access token (create one in the Bitwarden web app: Secrets Manager → Machine accounts → Access tokens).  Wrong region?  Re-run `hermes secrets bitwarden setup` and pick EU/self-hosted.)r   r  AUTH_EXPIREDsuperremediation)r  rF  r~  	__class__s      r    r  zBitwardenSource.remediation  sE    I)9+ABBBL  ww""4---r"   )r~  r'  r   r9   )r~  r'  )r   r'  )r~  r'  r%   r   r   r   )r~  r'  r   r   )rd  
__module____qualname____doc__r   labelshapeschemerk  r  r  r  r  __classcell__)r  s   @r    r{  r{  G  s        
 
 D'EEFR R R R& & & &
 
 
 
BI I I IV. . . . . . . . . .r"   r{  messager   c                (   |                                  dv rt          j        S dv sdv rt          j        S t	          fddD                       rt          j        S t	          fddD                       rt          j        S t          j        S )zABest-effort mapping of bws failure text onto the shared taxonomy.z	timed outzbinary not availablezfailed to invokec              3      K   | ]}|v V  	d S Nr   r   toklowereds     r    	<genexpr>z&_classify_bws_error.<locals>.<genexpr>  s7       : :c3'> : : : : : :r"   )unauthorizedzinvalid tokenzaccess token401403invalid_clientinvalid_grantz400 bad requestc              3      K   | ]}|v V  	d S r  r   r  s     r    r  z&_classify_bws_error.<locals>.<genexpr>  s7       : :c3'> : : : : : :r"   )network
connectionresolvedownloaddns)r_   r   r@  r  anyr  r?  INTERNAL)r  r  s    @r    r>  r>    s    mmooGg  ((,>',I,I''
 : : : : &9 : : : : : % $$
 : : : : &9 : : : : : !  r"   c                    t                                            t                              |            	 t          |                                            dS # t
          t          f$ r Y dS w xY w)aY  Drop in-process AND disk caches (plaintext and encrypted).

    Used after a token rotation (`hermes secrets bitwarden token`) so the
    next startup fetches fresh with the new credential instead of serving
    a pull cached under the old token's fingerprint.  The encrypted cache
    is keyed off the old token too, so it must go as well.
    N)r   clearr$   r0   r  r  rf   r)   s    r    clear_cachesr    sq     LLNNNi   "9--4466666w'   s   !A A-,A-c                $    t          |            dS )zClear in-process AND disk caches.

    Tests can pass ``home_path`` to scope the disk cleanup to a tmpdir.
    Without it we fall back to the same default resolution as the cache
    writer itself.
    N)r  r)   s    r    _reset_cache_for_testsr    s     r"   )r   r   r   r   r  )r%   r&   r   r   )r   r   )r8   r9   r   r&   )r   r   )rm   r9   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   )rX   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   r3  r&   r4  r  r5  r9   r   r   r%   r&   r6  r9   r7  r  r   r8  )r   r   r   r   )r   )
r;   r   r   r   r   r   r   r   r   r8  )rm  r9   rj  r   r   r   rk  r9   r4  r  rl  r9   r   r   r%   r&   r6  r9   r7  r  r   r   )r  r   r   r   )r%   r&   r   r   )Vr  
__future__r   r   r   r  loggingr>   rM   rM  rA   r   ra   rx   r+  urllib.errorr   urllib.requestr~   pathlibr   typingr   r   r   r   cryptography.hazmat.primitivesr	   +cryptography.hazmat.primitives.ciphers.aeadr
   'cryptography.hazmat.primitives.kdf.hkdfr   r.   r   r   r   r   r   re  agent.secret_sources.baser   r   	getLoggerrd  rE   r`   rv   rw   r   r`  r   r   r   __annotations___DISK_CACHE_BASENAMEr/   r  r   r!   r$   r*   r0   r6   rJ   r<   rl   rC   r{   r|   r}   r   r   r   r   r   r   r  r1  rI  rR  r=  ry  r{  r>  r  r  r   r"   r    <module>r     sN    : # " " " " "     				  				                        . . . . . . . . . . . . 1 1 1 1 1 1 > > > > > > 8 8 8 8 8 8            > = = = = = = =		8	$	$  Q,PP  @\???     #sC- 	(* * * * * ( 0  8 3 3 3 3 #      
' ' ' ' 'O O O O O% % % % ,1      6B B B B( ( ( (V "' : : : : : :zH H H H          "   @B B B B
1 1 1 1A A A A+ + + + !%= = = = = =J !%+ + + + + +d "" $$)/0G G G G G GT1 1 1 1D FHG G G G Gd /#" $$)/0V V V V V V|S. S. S. S. S.l S. S. S.l   6           r"   