
    iL                        d 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mZ  ej                    dk    Z ej        e          ZdedefdZdZdefd	Z e            Zdd
edz  dedz  defdZdefdZeZdZdedefdZdeee         e f         fdZ!dee         fdZ"dedee         defdZ# G d de          Z$dS )uE   Local execution environment — spawn-per-call with session snapshot.    N)BaseEnvironment_pipe_stdinWindowscwdreturnc                 N   | r!t           j                            |           r| S | rt           j                            |           nd}|rKt           j                            |          r|S t           j                            |          }||k    rn|}|Kt	          j                    S )ur  Return ``cwd`` if it exists as a directory, else the nearest existing
    ancestor.  Falls back to ``tempfile.gettempdir()`` only if walking up the
    path can't find any existing directory (effectively never on a healthy
    filesystem, but cheap belt-and-braces).

    Used by ``_run_bash`` to recover when the configured cwd is gone — most
    commonly because a previous tool call deleted its own working directory
    (issue #17558).  Without this guard, ``subprocess.Popen(..., cwd=...)``
    raises ``FileNotFoundError`` before bash starts, wedging every subsequent
    terminal call until the gateway restarts.
     )ospathisdirdirnametempfile
gettempdir)r   parentnext_parents      =/home/piyush/.hermes/hermes-agent/tools/environments/local.py_resolve_safe_cwdr      s      rw}}S!! 
%(0RW__S!!!bF
 7==   	Mgoof--&             _HERMES_FORCE_c                  `   t                      } 	 ddlm} |                                D ]=}|                     |j                   |j        r|                     |j                   >n# t          $ r Y nw xY w	 ddl	m
} |                                D ]d\  }}|                    d          }|dv r|                     |           4|dk    r*|                    d          r|                     |           en# t          $ r Y nw xY w|                     h d           t          |           S )	z=Derive the blocklist from provider, tool, and gateway config.r   )PROVIDER_REGISTRY)OPTIONAL_ENV_VARScategory>   tool	messagingsettingpassword>?   GH_TOKENHASS_URL	LLM_MODEL
HASS_TOKENXAI_API_KEYGROQ_API_KEYVERCEL_TOKENEMAIL_ADDRESSGITHUB_APP_IDOPENAI_ORG_IDWHATSAPP_MODECOHERE_API_KEYEMAIL_PASSWORDGOOGLE_API_KEYMODAL_TOKEN_IDOPENAI_API_KEYSIGNAL_ACCOUNTVERCEL_TEAM_IDANTHROPIC_TOKENDAYTONA_API_KEYEMAIL_IMAP_HOSTEMAIL_SMTP_HOSTMISTRAL_API_KEYOPENAI_API_BASEOPENAI_BASE_URLSIGNAL_HTTP_URLDEEPSEEK_API_KEYHELICONE_API_KEYPARALLEL_API_KEYTOGETHER_API_KEYWHATSAPP_ENABLEDFIRECRAWL_API_KEYFIRECRAWL_API_URLFIREWORKS_API_KEYVERCEL_OIDC_TOKENVERCEL_PROJECT_IDANTHROPIC_BASE_URLEMAIL_HOME_ADDRESSMODAL_TOKEN_SECRETOPENROUTER_API_KEYPERPLEXITY_API_KEYSLACK_HOME_CHANNELDISCORD_AUTO_THREADOPENAI_ORGANIZATIONSIGNAL_HOME_CHANNELSLACK_ALLOWED_USERSDISCORD_HOME_CHANNELSIGNAL_ALLOWED_USERSGATEWAY_ALLOWED_USERSSIGNAL_IGNORE_STORIESTELEGRAM_HOME_CHANNELWHATSAPP_ALLOWED_USERSCLAUDE_CODE_OAUTH_TOKENDISCORD_REQUIRE_MENTIONEMAIL_HOME_ADDRESS_NAMESLACK_HOME_CHANNEL_NAMESIGNAL_HOME_CHANNEL_NAMEDISCORD_HOME_CHANNEL_NAMEGITHUB_APP_INSTALLATION_IDSIGNAL_GROUP_ALLOWED_USERSTELEGRAM_HOME_CHANNEL_NAMEGITHUB_APP_PRIVATE_KEY_PATHDISCORD_FREE_RESPONSE_CHANNELS)sethermes_cli.authr   valuesupdateapi_key_env_varsbase_url_env_varaddImportErrorhermes_cli.configr   itemsget	frozenset)blockedr   pconfigr   namemetadatar   s          r   _build_provider_env_blocklistrm   3   s   G555555(//11 	6 	6GNN73444' 6G4555	6    	777777/5577 	" 	"ND(||J//H000D!!!!Y&&8<<
+C+C&D!!!	"     NN @ @ @ @ @ @B Ws%   AA) )
A65A6:A?C: :
DDbase_env	extra_envc                    	 ddl m} n# t          $ r d }Y nw xY wi }| pi                                 D ]9\  }}|                    t
                    r |t          vs ||          r|||<   :|pi                                 D ]Z\  }}|                    t
                    r"|t          t
                    d         }|||<   A|t          vs ||          r|||<   [ddlm	}  |            }|r||d<   |S )z<Filter Hermes-managed secrets from a subprocess environment.r   is_env_passthroughc                     dS NF _s    r   <lambda>z*_sanitize_subprocess_env.<locals>.<lambda>       E r   Nget_subprocess_homeHOME)
tools.env_passthroughrr   	Exceptionrf   
startswith!_HERMES_PROVIDER_ENV_FORCE_PREFIX_HERMES_PROVIDER_ENV_BLOCKLISTlenhermes_constantsr{   )	rn   ro   _is_passthrough	sanitizedkeyvaluereal_keyr{   _profile_homes	            r   _sanitize_subprocess_envr      sf   *OOOOOOO * * *)/* !#I~2,,.. # #
U>>;<< 	4448L8L4"IcN B--// # #
U>>;<< 	#3@AABBCH"'Ih666//#:N:N6"IcN 544444''))M *)	&   	 c            	         t           s{t          j        d          pft          j                            d          rdndpCt          j                            d          rdndp t          j                            d          pdS t          j                            d          } | r!t          j                            |           r| S t          j        d          }|r|S t          j                            t          j                            dd	          d
dd          t          j                            t          j                            dd          d
dd          t          j                            t          j                            dd          dd
dd          fD ]'}|r#t          j                            |          r|c S (t          d          )z Find bash for command execution.bashz/usr/bin/bashNz	/bin/bashSHELLz/bin/shHERMES_GIT_BASH_PATHProgramFileszC:\Program FilesGitbinzbash.exezProgramFiles(x86)zC:\Program Files (x86)LOCALAPPDATAr	   ProgramszGit Bash not found. Hermes Agent requires Git for Windows on Windows.
Install it from: https://git-scm.com/download/win
Or set HERMES_GIT_BASH_PATH to your bash.exe location.)
_IS_WINDOWSshutilwhichr
   r   isfileenvironrg   joinRuntimeError)customfound	candidates      r   
_find_bashr      s    
L   #%7>>/#B#BL!w~~k::D z~~g&& 	
 Z^^233F "'..(( L  E  	RZ^^N4GHH%QVXbcc
RZ^^$79RSSUZ\acmnn
RZ^^NB77UES]^^  	
  		22 	
	A  r   za/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binenvc                    	 ddl m} n# t          $ r d }Y nw xY wt          t          j        | z            }i }|                                D ]Z\  }}|                    t                    r"|t          t                    d         }|||<   A|t          vs ||          r|||<   [|                    dd          }d|                    d          vr|r| dt           nt          |d<   dd	lm}  |            }	|	r|	|d
<   |S )zDBuild a run environment with a sane PATH and provider-var stripping.r   rq   c                     dS rt   ru   rv   s    r   rx   z_make_run_env.<locals>.<lambda>   ry   r   NPATHr	   z/usr/bin:rz   r|   )r}   rr   r~   dictr
   r   rf   r   r   r   r   rg   split
_SANE_PATHr   r{   )
r   r   mergedrun_envkvr   existing_pathr{   r   s
             r   _make_run_envr      s^   *OOOOOOO * * *)/* "*s"##FG  1<<9:: 	>??@@AH !GH4448J8J4GAJKK++M,,S1111=JZ]99Z999PZ
 544444''))M ('Nr   c                  <   	 ddl m}   |             pi }|                    d          pi }|                    d          pg }t          |t                    sg }t          |                    dd                    }d |D             |fS # t          $ r g dfcY S w xY w)u   Return (shell_init_files, auto_source_bashrc) from config.yaml.

    Best-effort — returns sensible defaults on any failure so terminal
    execution never breaks because the config file is unreadable.
    r   )load_configterminalshell_init_filesauto_source_bashrcTc                 0    g | ]}|t          |          S ru   )str).0fs     r   
<listcomp>z4_read_terminal_shell_init_config.<locals>.<listcomp>  s#    +++1+A+++r   )re   r   rg   
isinstancelistboolr~   )r   cfgterminal_cfgfilesauto_bashrcs        r    _read_terminal_shell_init_configr      s    111111kmm!rwwz**0b  !344:%&& 	E<++,@$GGHH+++++[88   4xs   BB
 
BBc                     t                      \  } }g }| r|                    |            n |rt          s|                    g d           g }|D ]}	 t          j                            t          j                            |                    }n# t          $ r Y Lw xY w|r4t          j                            |          r|	                    |           |S )uh  Resolve the list of files to source before the login-shell snapshot.

    Expands ``~`` and ``${VAR}`` references and drops anything that doesn't
    exist on disk, so a missing ``~/.bashrc`` never breaks the snapshot.
    The ``auto_source_bashrc`` path runs only when the user hasn't supplied
    an explicit list — once they have, Hermes trusts them.
    )z
~/.profilez~/.bash_profilez	~/.bashrc)
r   extendr   r
   r   
expandvars
expanduserr~   r   append)explicitr   
candidatesresolvedrawr   s         r   _resolve_shell_init_filesr     s     =>>HkJ J(####	 J[ J  	HHHIIIH " "	7%%bg&8&8&=&=>>DD 	 	 	H	 	"BGNN4(( 	"OOD!!!Os   <B
BB
cmd_stringr   c                     |s| S dg}|D ]4}|                     dd          }|                    d| d| d           5d                    |          dz   }|| z   S )a  Prepend ``source <file>`` lines (guarded + silent) to a bash script.

    Each file is wrapped so a failing rc file doesn't abort the whole
    bootstrap: ``set +e`` keeps going on errors, ``2>/dev/null`` hides
    noisy prompts, and ``|| true`` neutralises the exit status.
    zset +e'z'\''z[ -r 'z
' ] && . 'z' 2>/dev/null || true
)replacer   r   )r   r   prelude_partsr   safepreludes         r   _prepend_shell_initr   :  s      JM S S ||C))QdQQdQQQRRRRii&&-GZr   c                        e Zd ZdZddededef fdZd	efd
Zdddddede	dededz  d	e
j        f
dZd ZdefdZd Z xZS )LocalEnvironmentzRun commands directly on the host machine.

    Spawn-per-call: every execute() spawns a fresh bash process.
    Session snapshot preserves env vars across calls.
    CWD persists via file-based read after each command.
    r	   <   Nr   timeoutr   c                     |rt           j                            |          }t                                          |pt          j                    ||           |                                  d S )N)r   r   r   )r
   r   r   super__init__getcwdinit_session)selfr   r   r   	__class__s       r   r   zLocalEnvironment.__init__W  sb     	*'$$S))CS/BIKKcJJJr   r   c                    dD ]k}| j                             |          pt          j                            |          }|r.|                    d          r|                    d          pdc S lt          j                            d          r.t          j        dt          j	        t          j
        z            rdS t          j                    }|                    d          r|                    d          pdS dS )a6  Return a shell-safe writable temp dir for local execution.

        Termux does not provide /tmp by default, but exposes a POSIX TMPDIR.
        Prefer POSIX-style env vars when available, keep using /tmp on regular
        Unix systems, and only fall back to tempfile.gettempdir() when it also
        resolves to a POSIX path.

        Check the environment configured for this backend first so callers can
        override the temp root explicitly (for example via terminal.env or a
        custom TMPDIR), then fall back to the host process environment.
        )TMPDIRTMPTEMP/z/tmp)r   rg   r
   r   r   rstripr   r   accessW_OKX_OKr   r   )r   env_varr   s      r   get_temp_dirzLocalEnvironment.get_temp_dir]  s     1 	4 	4GW--H1H1HI 4Y11#66 4 '',,33337==   	RYvrw7H%I%I 	6'))	$$ 	0##C((/C/vr   Fx   )loginr   
stdin_datar   r   r   c                D   t                      }|r t                      }|rt          ||          }|r|dd|gn|d|g}t          | j                  }t          | j                  }	|	| j        k    r(t                              d| j        |	           |	| _        | j        }
t          rS|
rQt          j        d|
          r<|
d                                         dz   |
dd                              dd	          z   }
t          j        |d
|ddt          j        t          j        |t          j        nt          j        t          rd nt&          j        |

  
        }t          s0	 t'          j        |j                  |_        n# t0          $ r Y nw xY w|t3          ||           |S )Nz-lz-czaLocalEnvironment cwd %r is missing on disk; falling back to %r so terminal commands keep working.z^/[a-zA-Z]/   r      r   \Tzutf-8r   )	textr   encodingerrorsstdoutstderrstdin
preexec_fnr   )r   r   r   r   r   r   r   loggerwarningr   rematchupperr   
subprocessPopenPIPESTDOUTDEVNULLr
   setsidgetpgidpid_hermes_pgidProcessLookupErrorr   )r   r   r   r   r   r   
init_filesargsr   safe_cwd
_popen_cwdprocs               r   	_run_bashzLocalEnvironment._run_bashw  s    ||  	I244J I0ZHH
16TdD*--T4<T)) %TX..txNNH	    DH X
 	Y: 	Y"(>:*N*N 	Y#A,,..4z!""~7M7McSW7X7XXJ?$%/%;*//AS*9tt	
 
 
  	$&Jtx$8$8!!%    !j)))s   E> >
F
Fc                    dt           dt          fddt           dt          dt          ffd}	 t          r                                 dS 	 t          j        j                  }n$# t          $ r t          dd          }| Y nw xY w	 t          j
        |t          j                   n# t          $ r Y dS w xY w ||d          rdS 	 t          j
        |t          j                   n# t          $ r Y dS w xY w ||d	           	                     d
           dS # t          j        t"          f$ r Y dS w xY w# t          t$          t"          f$ r+ 	                                  Y dS # t(          $ r Y Y dS w xY ww xY w)z-Kill the entire process group (all children).pgidr   c                 l    	 t          j        | d           dS # t          $ r Y dS t          $ r Y dS w xY w)Nr   TF)r
   killpgr  PermissionError)r  s    r   _group_alivez4LocalEnvironment._kill_process.<locals>._group_alive  sY    	$"""t%   uu"   tts    
3	33r   c                 ~   t          j                    |z   }t          j                    |k     r^	                                  n# t          $ r Y nw xY w |           sdS t          j        d           t          j                    |k     ^	                                  n# t          $ r Y nw xY w |            S )NTg?)time	monotonicpollr~   sleep)r  r   deadliner  r
  s      r   _wait_for_group_exitz<LocalEnvironment._kill_process.<locals>._wait_for_group_exit  s    ~'''1H.""X--IIKKKK    D#|D))  4
4    .""X--		   #|D))))s#   A 
AAB# #
B0/B0r  Ng      ?g       @g?)r   )intr   floatr   	terminater
   r  r  r  getattrr  signalSIGTERMSIGKILLwaitr   TimeoutExpiredOSErrorr  killr~   )r   r
  r  r  r  s    `  @r   _kill_processzLocalEnvironment._kill_process  s&   		s 		t 		 		 		 			*s 	*U 	*t 	* 	* 	* 	* 	* 	* 	*$$	      :dh//DD)   "4>>D| $|
IdFN3333)   FF ('c22 FIdFN3333)   FF$$T3///IIcI*****"17;   DD"OW= 	 	 			   	s   E A* )E *BE 
BE B/ .E /
B=9E <B==E C/ .E /
C=9E <C==E D% %D?;E >D??E FE11
F ;F?F  Fresultc                 V   	 t          | j                  5 }|                                                                }ddd           n# 1 swxY w Y   |r&t          j                            |          r|| _        n# t          t          f$ r Y nw xY w| 
                    |           dS )ur  Read CWD from temp file (local-only, no round-trip needed).

        Skip the assignment when the path no longer exists as a directory —
        ``pwd -P`` on a deleted cwd can leave a stale value in the marker
        file, and propagating it would re-wedge the next ``Popen``.  The
        ``_run_bash`` recovery path will resolve a safe fallback if needed.
        N)open	_cwd_filereadstripr
   r   r   r   r"  FileNotFoundError_extract_cwd_from_output)r   r%  r   cwd_paths       r   _update_cwdzLocalEnvironment._update_cwd  s    	dn%% ,6688>>++, , , , , , , , , , , , , , , $BGMM(33 $#*+ 	 	 	D	 	%%f-----s3   A= 'A	A= 	AA= A+A= =BBc                 r    | j         | j        fD ]'}	 t          j        |           # t          $ r Y $w xY wdS )zClean up temp files.N)_snapshot_pathr(  r
   unlinkr"  )r   r   s     r   cleanupzLocalEnvironment.cleanup  sY    %t~6 	 	A	!   	 	s   '
44)r	   r   N)__name__
__module____qualname____doc__r   r  r   r   r   r   r   r   r  r$  r.  r2  __classcell__)r   s   @r   r   r   O  s         C s d      c    4 ;@!$+/; ; ;C ;4 ;;!Dj;4>4D; ; ; ;zD D DL.$ . . . .&      r   r   )N)%r6  loggingr
   platformr   r   r  r   r   r  tools.environments.baser   r   systemr   	getLoggerr3  r   r   r   r   rh   rm   r   r   r   r   _find_shellr   r   tupler   r   r   r   r   r   ru   r   r   <module>r?     s3   K K  				  				         @ @ @ @ @ @ @ @ho9,		8	$	$!3 !3 ! ! ! !8 %5 !Yy Y Y Y Yx "?!>!@!@  td{ td{ VZ    >C    F 
C t     <%S	4*@    ('49 ' ' ' 'T C  S	  c        *D D D D D D D D D Dr   