
    )jA                    f   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 ddlm	Z	 ddl
mZmZmZ ddlmZ  ej        e          Z ed          Zd	ed
<   d1dZdaded<   d2dZ	 d3d4dZ	 d3d5dZd6dZd6dZ	 d3d7d Zdad!ed"<   d8d$Z	 d3d9d%Zg d&Z d'ed(<   	 d3d9d)Z!	 d3d:d,Z"	 d3d;d-Z#	 d3d9d.Z$d<d0Z%dS )=u  File passthrough registry for remote terminal backends.

Remote backends (Docker, Modal, SSH) create sandboxes with no host files.
This module ensures that credential files, skill directories, and host-side
cache directories (documents, images, audio, screenshots) are mounted or
synced into those sandboxes so the agent can access them.

**Credentials and skills** — session-scoped registry fed by skill declarations
(``required_credential_files``) and user config (``terminal.credential_files``).

**Cache directories** — gateway-cached uploads, browser screenshots, TTS
audio, and processed images.  Mounted read-only so the remote terminal can
reference files the host side created (e.g. ``unzip`` an uploaded archive).

Remote backends call :func:`get_credential_file_mounts`,
:func:`get_skills_directory_mount` / :func:`iter_skills_files`, and
:func:`get_cache_directory_mounts` / :func:`iter_cache_files` at sandbox
creation time and before each command (for resync on Modal).
    )annotationsN)
ContextVar)Path)DictListOptional)cfg_get_registered_fileszContextVar[Dict[str, str]]_registered_files_varreturnDict[str, str]c                     	 t                                           S # t          $ r! i } t                               |            | cY S w xY w)zSGet or create the registered credential files dict for the current context/session.)r   getLookupErrorset)vals    =/home/wildlama/.hermes/hermes-agent/tools/credential_files.py_get_registeredr   &   sV    $((***    !!#&&&


s    (AAzList[Dict[str, str]] | None_config_filesr   c                 "    ddl m}   |             S )Nr   get_hermes_home)hermes_constantsr   r   s    r   _resolve_hermes_homer   4   s"    000000?    /root/.hermesrelative_pathstrcontainer_baseboolc                :   t                      }t          j                            |           rt                              d|            dS || z  }ddlm}  |||          }|rt                              d| |           dS |                                }|	                                st          
                    d|           dS |                    d           d|  }t          |          t                      |<   t          
                    d||           d	S )
a  Register a credential file for mounting into remote sandboxes.

    *relative_path* is relative to ``HERMES_HOME`` (e.g. ``google_token.json``).
    Returns True if the file exists on the host and was registered.

    Security: rejects absolute paths and path traversal sequences (``..``).
    The resolved host path must remain inside HERMES_HOME so that a malicious
    skill cannot declare ``required_credential_files: ['../../.ssh/id_rsa']``
    and exfiltrate sensitive host files into a container sandbox.
    zMcredential_files: rejected absolute path %r (must be relative to HERMES_HOME)Fr   validate_within_dirz1credential_files: rejected path traversal %r (%s)z)credential_files: skipping %s (not found)/z%credential_files: registered %s -> %sT)r   ospathisabsloggerwarningtools.path_securityr#   resolveis_filedebugrstripr   r   )r   r   hermes_home	host_pathr#   containment_errorresolvedcontainer_paths           r   register_credential_filer4   9   s;    '((K 
w}}]## [	
 	
 	
 um+I 877777++I{CC ?	
 	
 	

 u  ""H @(KKKu&--c22DD]DDN(+HOn%
LL8(NSSS4r   entrieslist	List[str]c                b   g }| D ]}t          |t                    r|                                }nUt          |t                    r?|                    d          p|                    d          pd                                }n|st          ||          s|                    |           |S )zRegister multiple credential files from skill frontmatter entries.

    Each entry is either a string (relative path) or a dict with a ``path``
    key.  Returns the list of relative paths that were NOT found on the host
    (i.e. missing files).
    r&   name )
isinstancer   stripdictr   r4   append)r5   r   missingentryrel_paths        r   register_credential_filesrB   k   s     G 
% 
%eS!! 	{{}}HHt$$ 			&))DUYYv->->D"KKMMHH 	'.AA 	%NN8$$$Nr   List[Dict[str, str]]c                 0   t           t           S g } 	 ddlm} t                      } |            }t	          |dd          }t          |t                    rddlm} |D ]}t          |t                    r|
                                r|
                                }t          j                            |          rt                              d|           z||z  } |||          }	|	rt                              d||	           |                                }
|
                                r*d	| }|                     t          |
          |d
           n2# t&          $ r%}t                              d|           Y d}~nd}~ww xY w| a t           S )z=Load ``terminal.credential_files`` from config.yaml (cached).Nr   )read_raw_configterminalcredential_filesr"   z2credential_files: rejected absolute config path %rz8credential_files: rejected config path traversal %r (%s)z/root/.hermes/r0   r3   z8Could not read terminal.credential_files from config: %s)r   hermes_cli.configrE   r   r	   r;   r6   r*   r#   r   r<   r%   r&   r'   r(   r)   r+   r,   r>   	Exception)resultrE   r/   cfg
cred_filesr#   itemrelr0   r1   resolved_pathr3   es                r   _load_config_filesrR      s     #%F V555555*,,oS*.@AA
j$'' 	??????"  dC(( TZZ\\ **,,Cw}}S)) !PRU   ! +c 1I(;(;I{(S(S%( !V!2   !$-$5$5$7$7M$,,.. )?#)?)?),]););.<' '     V V VQSTUUUUUUUUV Ms   E
E 
F'FFc                 x   i } t                                                      D ]+\  }}t          |                                          r|| |<   ,t	                      D ]@}|d         }|| vr2t          |d                                                   r|d         | |<   Ad |                                 D             S )zReturn all credential files that should be mounted into remote sandboxes.

    Each item has ``host_path`` and ``container_path`` keys.
    Combines skill-registered files and user config.
    r3   r0   c                    g | ]
\  }}||d S )rH    ).0cphps      r   
<listcomp>z.get_credential_file_mounts.<locals>.<listcomp>   s4       B B//  r   )r   itemsr   r,   rR   )mountsr3   r0   r@   rW   s        r   get_credential_file_mountsr\      s      F &5%6%6%<%<%>%> / /!		??""$$ 	/%.F>" $%% , ,#$VU;%7 8 8 @ @ B B{+F2J llnn   r   list[Dict[str, str]]c                   g }t                      }|dz  }|                                r=t          |          }|                    ||                     d           dd           	 ddlm} t           |                      D ]X\  }}|                                r?t          |          }|                    ||                     d           d| d           Yn# t          $ r Y nw xY w|S )aM  Return mount info for all skill directories (local + external).

    Skills may include ``scripts/``, ``templates/``, and ``references/``
    subdirectories that the agent needs to execute inside remote sandboxes.

    **Security:** Bind mounts follow symlinks, so a malicious symlink inside
    the skills tree could expose arbitrary host files to the container.  When
    symlinks are detected, this function creates a sanitized copy (regular
    files only) in a temp directory and returns that path instead.  When no
    symlinks are present (the common case), the original directory is returned
    directly with zero overhead.

    Returns a list of dicts with ``host_path`` and ``container_path`` keys.
    The local skills dir mounts at ``<container_base>/skills``, external dirs
    at ``<container_base>/external_skills/<index>``.
    skillsr$   /skillsrH   r   get_external_skills_dirs/external_skills/)	r   is_dir_safe_skills_pathr>   r.   agent.skill_utilsrb   	enumerateImportError)r   r[   r/   
skills_dirr0   rb   idxext_dirs           r   get_skills_directory_mountrl      sI   & F&((Kx'J %j11	"!/!6!6s!;!;DDD
 
 	 	 	
>>>>>>%&>&>&@&@AA 	 	LC~~ -g66	!*)7)>)>s)C)C&[&[VY&[&[    	     Ms   (A6C 
C,+C,zPath | None_safe_skills_tempdirri   c                  	
 d |                      d          D             }|st          |           S |D ]0}t                              d|t	          j        |                     1ddl}ddl
ddl}t          r5t          
                                r
                    t          d           t          |                    d	                    		a	|                      d          D ]}|                                r|                    |           }	|z  }|
                                r|                    dd
           ]|                                rL|j                            dd
           
                    t          |          t          |                     	
fd}|                    |           t                              d	           t          	          S )z@Return *skills_dir* if symlink-free, else a sanitized temp copy.c                :    g | ]}|                                 |S rU   )
is_symlink)rV   ps     r   rY   z%_safe_skills_path.<locals>.<listcomp>   s%    CCCaALLNNCCCCr   *z:credential_files: skipping symlink in skills dir: %s -> %sr   NTignore_errorszhermes-skills-safe-)prefix)parentsexist_okc                 b                                      r                     d           d S d S )NTrs   )rd   rmtree)safe_dirshutils   r   _cleanupz#_safe_skills_path.<locals>._cleanup  s;    ?? 	8MM($M77777	8 	8r   z8credential_files: created symlink-safe skills copy at %s)rglobr   r(   r)   r%   readlinkatexitr{   tempfilerm   rd   ry   r   mkdtemprp   relative_tomkdirr,   parentcopy2registerinfo)ri   symlinkslinkr   r   rN   rO   targetr|   rz   r{   s            @@r   re   re      s    DC:++C00CCCH : 0 0SR[..	0 	0 	0 	0 MMMMMMOOO  @ 4 ; ; = = @*$???H$$,A$BBCCH#  %% 	1 	1?? 	z**C;;== 	1LLL5555\\^^ 	1Mt<<<LLTCKK0008 8 8 8 8 8 OOH
KKJHUUUx==r   c                P   g }t                      }|dz  }|                                r|                     d           d}|                    d          D ]j}|                                s|                                s+|                    |          }|                    t          |          | d| d           k	 ddl	m
} t           |                      D ]\  }}	|	                                s|                     d           d| }|	                    d          D ]j}|                                s|                                s+|                    |	          }|                    t          |          | d| d           kn# t          $ r Y nw xY w|S )	a>  Yield individual (host_path, container_path) entries for skills files.

    Includes both the local skills dir and any external dirs configured via
    skills.external_dirs.  Skips symlinks entirely.  Preferred for backends
    that upload files individually (Daytona, Modal) rather than mounting a
    directory.
    r_   r$   r`   rr   rH   r   ra   rc   )r   rd   r.   r}   rp   r,   r   r>   r   rf   rb   rg   rh   )
r   rK   r/   ri   container_rootrN   rO   rb   rj   rk   s
             r   iter_skills_filesr   &  s    $&F&((Kx'J 	*11#66???$$S)) 	 	D    "":..CMM YY%3";";c";";     >>>>>>%&>&>&@&@AA 	 	LC>>##  . 5 5c : :RRSRRNc**  ??$$ DLLNN &&w//!$T)7&?&?#&?&?     			     Ms   CF 
F#"F#))zcache/documentsdocument_cache)zcache/imagesimage_cache)zcache/audioaudio_cache)zcache/screenshotsbrowser_screenshotszlist[tuple[str, str]]_CACHE_DIRSc                    ddl m} g }t          D ]d\  }} |||          }|                                r?|                     d           d| }|                    t          |          |d           e|S )a   Return mount entries for each cache directory that exists on disk.

    Used by Docker to create bind mounts.  Each entry has ``host_path`` and
    ``container_path`` keys.  The host path is resolved via
    ``get_hermes_dir()`` for backward compatibility with old directory layouts.
    r   get_hermes_dirr$   rH   )r   r   r   rd   r.   r>   r   )r   r   r[   new_subpathold_namehost_dirr3   s          r   get_cache_directory_mountsr   b  s     0/////#%F!,  X!>+x88?? 	 . 5 5c : :JJ[JJNMM ]]"0     Mr   r0   Optional[str]c                   t          |           }t          |          D ]l}t          |d                   }	 |                    |          }n# t          $ r Y :w xY wt	          j        |d         |                                          c S dS )a  Map a host cache path to its mounted path under *container_base*.

    Returns the POSIX container path when *host_path* lives under one of the
    auto-mounted cache directories, otherwise ``None``.  Backend-agnostic: the
    caller decides which ``container_base`` applies (Docker ``/root/.hermes``,
    SSH ``<remote_home>/.hermes``, etc.) and whether translation is wanted.
    Always joins with ``posixpath`` because container/remote paths are POSIX
    regardless of the host OS.
    r   r0   r3   N)r   r   r   
ValueError	posixpathjoinas_posix)r0   r   r&   mountr   rO   s         r   map_cache_path_to_containerr   z  s     	??D+>JJJ G Gk*++	""8,,CC 	 	 	H	~e$45s||~~FFFFF4s   A
AAc                |    t           j                            dd          dk    r| S t          | |          }||n| S )a  Translate a host cache path to its mounted path inside the sandbox.

    Returns the input unchanged if it is not under any auto-mounted cache
    directory, or if the active terminal backend does not require path
    translation (only Docker for now).
    TERMINAL_ENVlocaldockerr   )r%   environr   r   )r0   r   mappeds      r   to_agent_visible_cache_pathr     sF     
z~~ng..(::(>RRRF'66Y6r   c                   ddl m} g }t          D ]\  }} |||          }|                                s&|                     d           d| }|                    d          D ]j}|                                s|                                s+|                    |          }|	                    t          |          | d| d           k|S )zReturn individual (host_path, container_path) entries for cache files.

    Used by Modal to upload files individually and resync before each command.
    Skips symlinks.  The container paths use the new ``cache/<subdir>`` layout.
    r   r   r$   rr   rH   )r   r   r   rd   r.   r}   rp   r,   r   r>   r   )	r   r   rK   r   r   r   r   rN   rO   s	            r   iter_cache_filesr     s    0/////#%F!,  X!>+x88   	*11#66FFFFNN3'' 	 	D    ""8,,CMM YY%3";";c";";     		 Mr   Nonec                 F    t                                                       dS )z8Reset the skill-scoped registry (e.g. on session reset).N)r   clearrU   r   r   clear_credential_filesr     s     r   )r   r   )r   r   )r   )r   r   r   r   r   r    )r5   r6   r   r   r   r7   )r   rC   )r   r   r   r]   )ri   r   r   r   )r   r   r   rC   )r0   r   r   r   r   r   )r0   r   r   r   r   r   )r   r   )&__doc__
__future__r   loggingr%   r   contextvarsr   pathlibr   typingr   r   r   rI   r	   	getLogger__name__r(   r   __annotations__r   r   r   r4   rB   rR   r\   rl   rm   re   r   r   r   r   r   r   r   rU   r   r   <module>r      s    ( # " " " " "  				     " " " " " "       ' ' ' ' ' ' ' ' ' ' % % % % % %		8	$	$ 5?J?R4S4S  S S S S    .2 1 1 1 1    */ / / / /h *    2* * * *Z   6 ** * * * *Z %)  ( ( ( (( ( ( (X *+ + + + +h& & &     *    4 *    4 *7 7 7 7 7, *    6     r   