
    KiD                     D   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 ej	                    dk    Z
ddlmZ ddlmZ ddlmZ 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Zded	efdZded	efdZ G d dee          Z dS )zHLocal execution environment with interrupt support and non-blocking I/O.    NWindows)BaseEnvironment)PersistentShellMixin)is_interrupted__HERMES_FENCE_a9f7b3___HERMES_FORCE_returnc                  `   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 )	a\  Derive the blocklist from provider, tool, and gateway config.

    Automatically picks up api_key_env_vars and base_url_env_var from
    every registered provider, plus tool/messaging env vars from the
    optional config registry, so new Hermes-managed secrets are blocked
    in subprocesses without having to maintain multiple static lists.
    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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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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          5/home/ubuntu/hermes-agent/tools/environments/local.py_build_provider_env_blocklistr^       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|||<   [|S )a?  Filter Hermes-managed secrets from a subprocess environment.

    `_HERMES_FORCE_<VAR>` entries in ``extra_env`` opt a blocked variable back in
    intentionally for callers that truly need it.  Vars registered via
    :mod:`tools.env_passthrough` (skill-declared or user-configured) also
    bypass the blocklist.
    r   is_env_passthroughc                     dS NF _s    r]   <lambda>z*_sanitize_subprocess_env.<locals>.<lambda>       E     N)tools.env_passthroughrc   	ExceptionrV   
startswith!_HERMES_PROVIDER_ENV_FORCE_PREFIX_HERMES_PROVIDER_ENV_BLOCKLISTlen)r_   r`   _is_passthrough	sanitizedkeyvaluereal_keys          r]   _sanitize_subprocess_envrw      s2   *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   	 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          )u   Find bash for command execution.

    The fence wrapper uses bash syntax (semicolons, $?, printf), so we
    must use bash — not the user's $SHELL which could be fish/zsh/etc.
    On Windows: uses Git Bash (bundled with Git for Windows).
    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)LOCALAPPDATA 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whichospathisfileenvironrW   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  rk   )z'bash: cannot set terminal process groupz"bash: no job control in this shellzno job control in this shellz!cannot set terminal process groupz)tcsetattr: Inappropriate ioctl for devicezRestored session:zSaving session...zLast login:zcommand not found:z	Oh My Zshz	compinit:outputc                 *   dt           dt          fd}|                     d          }|r9 ||d                   r(|                    d           |r ||d                   (t	          |          dz
  }|dk    r=||         r |||                   r$|dz  }|dk    r||          |||                   $|dk     rdS |d|dz            }d                    |          }|                     d          r|r|                    d          s|dz  }|S )	zStrip shell startup/exit warnings that leak when using -i without a TTY.

    Removes lines matching known noise patterns from both the beginning
    and end of the output.  Lines in the middle are left untouched.
    liner	   c                 D     t           fdt          D                       S )Nc              3       K   | ]}|v V  	d S Nrf   ).0noiser   s     r]   	<genexpr>z8_clean_shell_noise.<locals>._is_noise.<locals>.<genexpr>   s'      FFU5D=FFFFFFrk   )any_SHELL_NOISE_SUBSTRINGS)r   s   `r]   	_is_noisez%_clean_shell_noise.<locals>._is_noise   s'    FFFF.EFFFFFFrk   
r      r   N)strboolsplitpoprq   r   endswith)r   r   linesendcleanedresults         r]   _clean_shell_noiser      s]   G G G G G G LLE  IIeAh'' 		!  IIeAh''  e**q.C
((E#J())E#J*?*?(q ((E#J())E#J*?*?( QwwrIcAgIGYYwF t  0E0E $Mrk   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<   |S )	zDBuild a run environment with a sane PATH and provider-var stripping.r   rb   c                     dS re   rf   rg   s    r]   ri   z_make_run_env.<locals>.<lambda>  rj   rk   NPATHr   z/usr/bin:)rl   rc   rm   dictr   r   rV   rn   ro   rq   rp   rW   r   
_SANE_PATH)r   rr   mergedrun_envkvrv   existing_paths           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Nrx   rawc                    |                      t                    }|dk    rt          |           S |t          t                    z   }|                     t                    }||k    rt          | |d                   S | ||         S )az  Extract real command output from between fence markers.

    The execute() method wraps each command with printf(FENCE) markers.
    This function finds the first and last fence and returns only the
    content between them, which is the actual command output free of
    any shell init/exit noise.

    Falls back to pattern-based _clean_shell_noise if fences are missing.
    N)find_OUTPUT_FENCEr   rq   rfind)r   firststartlasts       r]   _extract_fenced_outputr   %  s|     HH]##E{{!#&&&C&&&E99]##Du}}!#eff+...uTz?rk   c                        e Zd ZdZ	 	 ddededed	ef fd
Ze	defd            Z
dej        fdZdedee         fdZd Zd Zdddddedededz  dedz  def
dZ xZS )LocalEnvironmenta  Run commands directly on the host machine.

    Features:
    - Popen + polling for interrupt support (user can cancel mid-command)
    - Background stdout drain thread to prevent pipe buffer deadlocks
    - stdin_data support for piping content (bypasses ARG_MAX limits)
    - sudo -S transform via SUDO_PASSWORD env var
    - Uses interactive login shell so full user env is available
    - Optional persistent shell mode (cwd/env vars survive across calls)
    r   <   NFcwdtimeoutr   
persistentc                     t                                          |pt          j                    ||           || _        | j        r|                                  d S d S )N)r   r   r   )super__init__r   getcwdr   _init_persistent_shell)selfr   r   r   r   	__class__s        r]   r   zLocalEnvironment.__init__I  s`    S/BIKKcJJJ$? 	*'')))))	* 	*rk   r	   c                     d| j          S )Nz/tmp/hermes-local-)_session_idr   s    r]   _temp_prefixzLocalEnvironment._temp_prefixP  s    6D$4666rk   c           	          t                      }t          | j                  }t          j        |dgt          j        t          j        t          j        d|t          rd nt          j	                  S )Nz-lT)stdinstdoutstderrtextr   
preexec_fn)
r   r   r   
subprocessPopenPIPEDEVNULLr   r   setsid)r   
user_shellr   s      r]   _spawn_shell_processz%LocalEnvironment._spawn_shell_processT  s`    \\
))/?%*9tt	
 
 
 	
rk   pathsc                    g }|D ]}t           j                            |          rOt          |          5 }|                    |                                           d d d            n# 1 swxY w Y   p|                    d           |S )Nr   )r   r   existsopenappendread)r   r   resultsr   fs        r]   _read_temp_filesz!LocalEnvironment._read_temp_filesa  s     	# 	#Dw~~d## #$ZZ -1NN16688,,,- - - - - - - - - - - - - - - r""""s   (A))A-	0A-	c                     | j         d S 	 t          j        ddt          | j                   gdd           d S # t          j        t
          f$ r Y d S w xY w)Npkillz-PT   )capture_outputr   )
_shell_pidr   runr   TimeoutExpiredFileNotFoundErrorr   s    r]   _kill_shell_childrenz%LocalEnvironment._kill_shell_childrenk  s}    ?"F	N$DO 4 45#Q      )+<= 	 	 	DD	s   ,9 AAc                     t          j         | j         d          D ]5}t          j                            |          rt          j        |           6d S )Nz-*)globr   r   r   r   remove)r   r   s     r]   _cleanup_temp_filesz$LocalEnvironment._cleanup_temp_filesv  sU    d/33344 	 	Aw~~a   	!	 	rk   )r   
stdin_datacommandr   c                   |p| j         pt          j                    }|p| j        }|                     |          \  }}||||z   n||n|t                      }	dt           d| dt           d}
t          | j                  }t          j
        |	d|
gd||ddt          j        t          j        t          j        nt          j        t          rd nt          j        	
  
        .fd
}t!          j        |d                                           g fd}t!          j        |d          }|                                 t'          j                    |z   }                                t-                      r	 t          r                                 nt          j        j                  }t          j        |t6          j                   	                     d           n4# t          j        $ r" t          j        |t6          j                   Y nw xY wn+# t@          tB          f$ r "                                 Y nw xY w|#                    d           d#                              dz   ddS t'          j                    |k    r	 t          r                                 n6t          j        t          j        j                  t6          j                   n+# t@          tB          f$ r "                                 Y nw xY w|#                    d           d#                              }d| d}|r||z   n|$                                ddS t'          j%        d                                           |#                    d           tM          d#                                        }|j'        dS )Nzprintf 'z'
z
__hermes_rc=$?
printf 'z'
exit $__hermes_rc
z-licTzutf-8replace)	r   r   r   encodingerrorsr   r   r   r   c                      	 j                                         j                                          d S # t          t          f$ r Y d S w xY wr   )r   writecloseBrokenPipeErrorOSError)effective_stdinprocs   r]   _write_stdinz7LocalEnvironment._execute_oneshot.<locals>._write_stdin  s_    J$$_555J$$&&&&&'1   DDs   38 AA)targetdaemonc                     	 j         D ]}                     |            n# t          $ r Y nw xY w	 j                                          d S # t          $ r Y d S w xY w# 	 j                                          w # t          $ r Y w w xY wxY wr   )r   r   
ValueErrorr   rm   )r   _output_chunksr   s    r]   _drain_stdoutz8LocalEnvironment._execute_oneshot.<locals>._drain_stdout  s    	 K 0 0D"))$////0   K%%'''''    DDK%%''''    DsV   # A  
0A  0A  A 
AA B"A<;B<
B	BB		Bg      ?)r      r   u2   
[Command interrupted — user sent a new message]   )r   
returncodez
[Command timed out after zs]|   g?r   )(r   r   r   r   _prepare_commandr   r   r   r   r   r   r   STDOUTr   r   r   	threadingThreadr   time	monotonicpollr   	terminategetpgidpidkillpgsignalSIGTERMwaitr   SIGKILLProcessLookupErrorPermissionErrorkillr   lstripsleepr   r	  )r   r   r   r   r   work_direffective_timeoutexec_command
sudo_stdinr   
fenced_cmdr   r   r  readerdeadlinepgidpartialtimeout_msgr   r  r   r   s                       @@@r]   _execute_oneshotz!LocalEnvironment._execute_oneshot{  s+    1$(1bikk#3t|#'#8#8#A#A j!j&<(:5OO#(OO(O\\
#} # ## # %# # # 	  )),?$%4%@*//jFX*9tt	
 
 
 &      L>>>DDFFF$&
	 
	 
	 
	 
	 
	 !tDDD>##&77iikk!  " <((((!z$(33	$777< IIcI2222)8 < < <IdFN;;;;;<*O<      IIKKKKK A&&& ggn558mm"%   ~(** " H((((	"*TX"6"6GGG*O<      IIKKKKK A&&&''.11Q<MQQQ7>Xg33KDVDVDXDX"%   JsOOOE iikk!H 	A'(?(?@@ @@@sJ   AH' G2 1H' 2.H# H' "H##H' '%IIAK. .%LL)r   r   NF)r   )__name__
__module____qualname____doc__r   intr   r   r   propertyr   r   r   r   listr   r   r   r)  __classcell__)r   s   @r]   r   r   =  ss       	 	 FJ$)* *C *s *d *!* * * * * * 7c 7 7 7 X7
j&6 
 
 
 
s tCy    	 	 	  
kA/326kA kA kA kA# kA"%*kA%(4ZkA;?kA kA kA kA kA kA kA kArk   r   r   )!r-  r   r   platformr   r  r   r  r  systemr   tools.environments.baser   #tools.environments.persistent_shellr   tools.interruptr   r   ro   rX   r^   rp   r   rw   r   r   _find_shellr   r   r   r   r   r   rf   rk   r]   <module>r8     s   N N  				            ho9, 3 3 3 3 3 3 D D D D D D * * * * * * * %5 !`y ` ` ` `F "?!>!@!@  td{ td{ VZ    >(C ( ( ( (X 
 "s s    HC t     *     0iA iA iA iA iA+_ iA iA iA iA iArk   