
    i>                    N   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
mZ ddlmZ  ej        e          Z ed          Zd	ed
<   d/dZdaded<   d0dZ	 d1d2dZ	 d1d3dZd4dZd4dZ	 d1d5d Zdad!ed"<   d6d$Z	 d1d7d%Zg d&Zd'ed(<   	 d1d7d)Z	 d1d8d+Z 	 d1d7d,Z!d9d.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)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/piyush/.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   3   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_filer3   8   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   r3   append)r4   r   missingentryrel_paths        r   register_credential_filesrA   j   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/r/   r2   z8Could not read terminal.credential_files from config: %s)r   hermes_cli.configrD   r   r   r:   r5   r)   r"   r   r;   r$   r%   r&   r'   r(   r*   r+   r=   	Exception)resultrD   r.   cfg
cred_filesr"   itemrelr/   r0   resolved_pathr2   es                r   _load_config_filesrQ      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.
    r2   r/   c                    g | ]
\  }}||d S )rG    ).0cphps      r   
<listcomp>z.get_credential_file_mounts.<locals>.<listcomp>   s4       B B//  r   )r   itemsr   r+   rQ   )mountsr2   r/   r?   rV   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#   /skillsrG   r   get_external_skills_dirs/external_skills/)	r   is_dir_safe_skills_pathr=   r-   agent.skill_utilsra   	enumerateImportError)r   rZ   r.   
skills_dirr/   ra   idxext_dirs           r   get_skills_directory_mountrk      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_tempdirrh   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 rT   )
is_symlink)rU   ps     r   rX   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 )NTrr   )rc   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atexitrz   tempfilerl   rc   rx   r   mkdtempro   relative_tomkdirr+   parentcopy2registerinfo)rh   symlinkslinkr~   r   rM   rN   targetr{   ry   rz   s            @@r   rd   rd      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_   rq   rG   r   r`   rb   )r   rc   r-   r|   ro   r+   r   r=   r   re   ra   rf   rg   )
r   rJ   r.   rh   container_rootrM   rN   ra   ri   rj   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#   rG   )r   r   r   rc   r-   r=   r   )r   r   rZ   new_subpathold_namehost_dirr2   s          r   get_cache_directory_mountsr   a  s     0/////#%F!,  X!>+x88?? 	 . 5 5c : :JJ[JJNMM ]]"0     Mr   r/   c                X   t           j                            dd          dk    r| S t          |           }t	          |          D ]c}t          |d                   }	 |                    |          }t          t          |d                   |z            c S # t          $ r Y `w xY w| 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docker)r   r/   r2   )r$   environr   r   r   r   r   
ValueError)r/   r   r%   mountr   rN   s         r   to_agent_visible_cache_pathr   y  s     
z~~ng..(::	??D+>JJJ  k*++	""8,,CtE"2344s:;;;;; 	 	 	H	s   9B
B'&B'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#   rq   rG   )r   r   r   rc   r-   r|   ro   r+   r   r=   r   )	r   r   rJ   r   r   r   r   rM   rN   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   clearrT   r   r   clear_credential_filesr     s     r   )r   r   )r   r   )r   )r   r   r   r   r   r   )r4   r5   r   r   r   r6   )r   rB   )r   r   r   r\   )rh   r   r   r   )r   r   r   rB   )r/   r   r   r   r   r   )r   r   )#__doc__
__future__r   loggingr$   contextvarsr   pathlibr   typingr   r   rH   r   	getLogger__name__r'   r
   __annotations__r   r   r   r3   rA   rQ   r[   rk   rl   rd   r   r   r   r   r   r   rT   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 *    : *    6     r   