
    (GjA                        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
Z
ddlmZmZ ddlmZ ddlmZmZmZ ddlmZ ddlmZ ddlmZ  ej        e          Z e ej        d	                    Zere                     ej!                    ej"                    Z#d
ee$gdf         dz  ddfdZ%dee$gdf         dz  fdZ&de'de$ddfdZ(defdZ)dej*        de$ddfdZ+	 d%de,e$         de$dz  dej*        fdZ-dede'fdZ.dede'ddfdZ/de$de0e1e2f         dz  fdZ3 G d de          Z4 G d d           Z5d!e$de$fd"Z6 G d# d$e          Z7dS )&aC  Base class for all Hermes execution environment backends.

Unified spawn-per-call model: every command spawns a fresh ``bash -c`` process.
A session snapshot (env vars, functions, aliases) is captured once at init and
re-sourced before each command. CWD persists via in-band stdout markers (remote)
or a temp file (local).
    N)ABCabstractmethod)Path)IOCallableProtocol)get_hermes_home)windows_hide_flags)is_interruptedHERMES_DEBUG_INTERRUPTcbreturnc                     | t           _        dS )z>Register a callback that _wait_for_process fires periodically.N)_activity_callback_localcallback)r   s    =/home/rurouni/.hermes/hermes-agent/tools/environments/base.pyset_activity_callbackr   /   s    (*%%%    c                  .    t          t          dd           S )Nr   )getattrr    r   r   _get_activity_callbackr   4   s    +Z>>>r   statelabelc                    t          j                    }|                     dd          }|| d         z
  |k     rdS || d<   	 t                      }|r+t	          || d         z
            } || d| d           dS dS # t
          $ r Y dS w xY w)a_  Fire the activity callback at most once every ``state['interval']`` seconds.

    *state* must contain ``last_touch`` (monotonic timestamp) and ``start``
    (monotonic timestamp of the operation start).  An optional ``interval``
    key overrides the default 10 s cadence.

    Swallows all exceptions so callers don't need their own try/except.
    intervalg      $@
last_touchNstartz (z
s elapsed))time	monotonicgetr   int	Exception)r   r   nowr   r   elapseds         r   touch_activity_if_duer&   8   s     .

CyyT**H
U<  8++E,#%% 	0#g.//GB%..7.../////	0 	0    s   9A> >
BBc                      t          j        d          } | rt          |           }nt                      dz  }|                    dd           |S )zReturn the host-side root for all sandbox storage (Docker workspaces,
    Singularity overlays/SIF cache, etc.).

    Configurable via TERMINAL_SANDBOX_DIR. Defaults to {HERMES_HOME}/sandboxes/.
    TERMINAL_SANDBOX_DIR	sandboxesTparentsexist_ok)osgetenvr   r	   mkdir)customps     r   get_sandbox_dirr2   R   sT     Y-..F ,LL+GGD4G(((Hr   procdatac                 f      fd}t          j        |d                                           dS )u  Write *data* to proc.stdin on a daemon thread to avoid pipe-buffer deadlocks.

    On Windows, text-mode stdin (``text=True`` / ``encoding="utf-8"``)
    translates ``\n`` → ``\r\n`` as the data flows through the pipe —
    which corrupts every write_file / patch call because the bytes that
    land on disk include injected carriage returns.  The file IS created,
    but every subsequent byte-count / content compare against the
    caller's ``\n``-only string fails.

    Workaround: write through ``proc.stdin.buffer`` (the underlying byte
    buffer), encoding to UTF-8 ourselves.  That bypasses Python's
    newline translation entirely on every platform.  No behaviour change
    on POSIX — the byte sequence is identical to what text-mode would
    produce there.
    c                     	 t          t                    r                    d          n} t          j        dj                  }|                    |            |                                 d S # t          t          f$ r Y d S w xY w)Nutf-8buffer)	
isinstancestrencoder   stdinwritecloseBrokenPipeErrorOSError)rawtargetr4   r3   s     r   _writez_pipe_stdin.<locals>._writew   s    	 +5T3*?*?I$++g&&&TCTZ4:>>FLLLLNNNNN) 	 	 	DD	s   A0A5 5B
	B
TrB   daemonN)	threadingThreadr   )r3   r4   rC   s   `` r   _pipe_stdinrH   f   sL    "      F40006688888r   cmd
stdin_datac                     |                     dt                                 t          j        | ft          j        t          j        |t          j        nt          j        dd|}|t          ||           |S )a  Spawn a subprocess with standard stdout/stderr/stdin setup.

    If *stdin_data* is provided, writes it asynchronously via :func:`_pipe_stdin`.
    Backends with special Popen needs (e.g. local's ``preexec_fn``) can bypass
    this and call :func:`_pipe_stdin` directly.
    creationflagsNT)stdoutstderrr<   text)
setdefaultr
   
subprocessPopenPIPESTDOUTDEVNULLrH   )rI   rJ   kwargsr3   s       r   _popen_bashrW      s     o'9';';<<< !+!7jooZ=O   D D*%%%Kr   pathc                     |                                  r9	 t          j        |                     d                    S # t          $ r Y nw xY wi S )z:Load a JSON file as a dict, returning ``{}`` on any error.r7   encoding)existsjsonloads	read_textr#   )rX   s    r   _load_json_storer`      sZ    {{}} 	:dnngn>>??? 	 	 	D	Is   '> 
A
Ac                     | j                             dd           |                     t          j        |d          d           dS )z.Write *data* as pretty-printed JSON to *path*.Tr*      )indentr7   rZ   N)parentr/   
write_textr]   dumps)rX   r4   s     r   _save_json_storerg      sH    KdT222OODJtA...OAAAAAr   	host_pathc                     	 t          |                                           }|j        |j        fS # t          $ r Y dS w xY w)zIReturn ``(mtime, size)`` for cache comparison, or ``None`` if unreadable.N)r   statst_mtimest_sizer@   )rh   sts     r   _file_mtime_keyrn      sO    )__!!##RZ((   tts   .1 
??c                       e Zd ZdZdedz  fdZd
dZddedz  defdZe	de
e         dz  fd            Ze	dedz  fd	            ZdS )ProcessHandlezDuck type that every backend's _run_bash() must return.

    subprocess.Popen satisfies this natively.  SDK backends (Modal, Daytona)
    return _ThreadedProcessHandle which adapts their blocking calls.
    r   Nc                     d S Nr   selfs    r   pollzProcessHandle.poll         r   c                     d S rr   r   rs   s    r   killzProcessHandle.kill   rv   r   timeoutc                     d S rr   r   rt   ry   s     r   waitzProcessHandle.wait   rv   r   c                     d S rr   r   rs   s    r   rM   zProcessHandle.stdout       (+r   c                     d S rr   r   rs   s    r   
returncodezProcessHandle.returncode   r~   r   r   Nrr   )__name__
__module____qualname____doc__r"   ru   rx   floatr|   propertyr   r:   rM   r   r   r   r   rp   rp      s          &cDj%%%%<<EDL<C<<<<+3$+++ X++C$J+++ X+++r   rp   c                       e Zd ZdZ	 ddeg eeef         f         deg df         dz  fdZe	d             Z
e	dedz  fd            Zdedz  fd	Zd
 Zddedz  defdZdS )_ThreadedProcessHandleal  Adapter for SDK backends (Modal, Daytona) that have no real subprocess.

    Wraps a blocking ``exec_fn() -> (output_str, exit_code)`` in a background
    thread and exposes a ProcessHandle-compatible interface.  An optional
    ``cancel_fn`` is invoked on ``kill()`` for backend-specific cancellation
    (e.g. Modal sandbox.terminate, Daytona sandbox.stop).
    Nexec_fn	cancel_fnc                 8    | _         t          j                     _        d  _        d  _        t          j                    \  }}t          j        |ddd           _	        | _
         fd}t          j        |d          }|                                 d S )Nrr7   replace)r[   errorsc                     	              \  } }|_         	 t          j        j        |                     dd                     n# t
          $ r Y nw xY wn%# t          $ r}|_        d_         Y d }~nd }~ww xY w	 t          j        j                   n# t
          $ r Y nw xY wj	        
                                 d S # 	 t          j        j                   n# t
          $ r Y nw xY wj	        
                                 w xY w)Nr7   r   r      )_returncoder-   r=   	_write_fdr;   r@   r#   _errorr>   _doneset)output	exit_codeexcr   rt   s      r   _workerz0_ThreadedProcessHandle.__init__.<locals>._worker   sO   !$+GII!	#, HT^V]]79]-U-UVVVV   D % % %!#$      %HT^,,,,   D
     	HT^,,,,   D
    s   A /A A 
AA AA C 
A;#A61C 6A;;C ?B 
B&%B&D
C D
 
C-*D
,C--D
TrD   )
_cancel_fnrF   Eventr   r   r   r-   pipefdopen_stdoutr   rG   r   )rt   r   r   read_fdwrite_fdr   ts   ``     r   __init__z_ThreadedProcessHandle.__init__   s    
 $_&&
'+(, GIIy#	RRR!	! 	! 	! 	! 	! 	!& GD999						r   c                     | j         S rr   )r   rs   s    r   rM   z_ThreadedProcessHandle.stdout   s
    |r   r   c                     | j         S rr   )r   rs   s    r   r   z!_ThreadedProcessHandle.returncode  s    r   c                 F    | j                                         r| j        nd S rr   )r   is_setr   rs   s    r   ru   z_ThreadedProcessHandle.poll  s#    #':#4#4#6#6@tD@r   c                 d    | j         r(	 |                                   d S # t          $ r Y d S w xY wd S rr   )r   r#   rs   s    r   rx   z_ThreadedProcessHandle.kill  sS    ? 	!!!!!   	 	s    
--ry   c                 F    | j                             |           | j        S )Nry   )r   r|   r   r{   s     r   r|   z_ThreadedProcessHandle.wait  s!    
(((r   rr   )r   r   r   r   r   tupler:   r"   r   r   rM   r   ru   rx   r   r|   r   r   r   r   r      s         04# #"eCHo-.# BH%,# # # #J   X  C$J       X AcDj A A A A     EDL  C            r   r   
session_idc                     d|  dS )N__HERMES_CWD___r   )r   s    r   _cwd_markerr     s    ):))))r   c                      e Zd ZU dZdZeed<   dZeed<   defdZ	d)d	ed
ede
fdZdddddeded
ededz  def
dZed             Zd Zed	edefd            Zded	edefdZedededefd            Zd*ded
ede
fdZdefdZde
fdZde
fdZd+d Z	 d,ddd"d#ded	ed
edz  dedz  d$ede
fd%Zd& Zd' Zdedeeedz  f         fd(ZdS )-BaseEnvironmenta  Common interface and unified execution flow for all Hermes backends.

    Subclasses implement ``_run_bash()`` and ``cleanup()``.  The base class
    provides ``execute()`` with session snapshot sourcing, CWD tracking,
    interrupt handling, and timeout enforcement.
    r   _stdin_mode   _snapshot_timeoutr   c                     dS )a,  Return the backend temp directory used for session artifacts.

        Most sandboxed backends use ``/tmp`` inside the target environment.
        LocalEnvironment overrides this on platforms like Termux where ``/tmp``
        may be missing and ``TMPDIR`` is the portable writable location.
        z/tmpr   rs   s    r   get_temp_dirzBaseEnvironment.get_temp_dir0  s	     vr   Ncwdry   envc                 X   || _         || _        |pi | _        t          j                    j        d d         | _        |                                                     d          pd}| d| j         d| _	        | d| j         d| _
        t          | j                  | _        d| _        d S )N   /z/hermes-snap-z.shz/hermes-cwd-z.txtF)r   ry   r   uuiduuid4hex_session_idr   rstrip_snapshot_path	_cwd_filer   _snapshot_ready)rt   r   ry   r   temp_dirs        r   r   zBaseEnvironment.__init__9  s    9":<<+CRC0$$&&--c229c!)MM8HMMM$HH$2BHHH&t'788$r   Fx   loginry   rJ   
cmd_stringr   rJ   c                J    t          t          |           j         d          )zSpawn a bash process to run *cmd_string*.

        Returns a ProcessHandle (subprocess.Popen or _ThreadedProcessHandle).
        Must be overridden by every backend.
        z must implement _run_bash())NotImplementedErrortyper   )rt   r   r   ry   rJ   s        r   	_run_bashzBaseEnvironment._run_bashI  s$     "T$ZZ%8"U"U"UVVVr   c                     dS )z<Release backend resources (container, instance, connection).Nr   rs   s    r   cleanupzBaseEnvironment.cleanupX  s	     	r   c                 V   |                      | j                  }t          j        | j                  }t          j        | j                  }t          j        | j        dz             dz   }d| d| d| d| d| d| d	| d
| d| d| d| d| j         d| j         d}	 |                     |d| j                  }| 	                    || j                  }t          |                    d          pd          dk    r%t          d|                    d                     d| _        |                     |           t                              d| j        | j                   dS # t$          $ r3}t                              d| j        |           d| _        Y d}~dS d}~ww xY w)zCapture login shell environment into a snapshot file.

        Called once after backend construction.  On success, sets
        ``_snapshot_ready = True`` so subsequent commands source the snapshot
        instead of running with ``bash -l``.
        .tmp.$BASHPIDzexport -p > z
__hermes_fns=$(declare -F | awk '{print $3}' | grep -vE '^_[^_]') || true
[ -n "$__hermes_fns" ] && declare -f $__hermes_fns >> z! 2>/dev/null || true
alias -p >> z#
echo 'shopt -s expand_aliases' >> z
echo 'set +e' >> z
echo 'set +u' >> z
mv -f  z
 || rm -f z
builtin cd -- z 2>/dev/null || true
pwd -P > z 2>/dev/null || true
printf '\n%sz\n' "$(pwd -P)"
T)r   ry   r   r   r   z)snapshot bootstrap failed with exit code z-Session snapshot created (session=%s, cwd=%s)uL   init_session failed (session=%s): %s — falling back to bash -l per commandFN)_quote_cwd_for_cdr   shlexquoter   r   r   r   r   _wait_for_processr"   r!   RuntimeErrorr   _update_cwdloggerinfor   r#   warning)	rt   _quoted_cwd_quoted_snap_quoted_cwd_file	_snap_tmp	bootstrapr3   resultr   s	            r   init_sessionzBaseEnvironment.init_sessiona  s     ,,TX66 {4#677 ;t~66$ K 3g =>>K	U9 U U U U  %!U U" 2;#U U$ !*%U U& !*'U U, -U U, ".-U U, :C-U U. )/U U0 )1U U2 *3U U2 /3.>3U U U 	8	)>>)4AW>XXD++D$:P+QQF6::l++0q11Q66"Z

<@X@XZZ   $(D V$$$KK?     
  	) 	) 	)NN6 	   $)D       	)s   !CE+ +
F(5(F##F(c                     | dk    r| S | dk    rdS |                      d          rdt          j        | dd                    S t          j        |           S )z7Quote a ``cd`` target while preserving ``~`` expansion.~z~/z$HOMEz$HOME/rb   N)
startswithr   r   )r   s    r   r   z!BaseEnvironment._quote_cwd_for_cd  sd     #::J$;;7>>$ 	32EKABB00222{3r   commandc                    |                     dd          }t          j        | j                  }t          j        | j                  }t          j        | j        dz             dz   }g }| j        r|                    d| d           |                     |          }|                    d| d           |                    d	| d           |                    d
           | j        r"|                    d| d| d| d| d	           |                    d| d           |                    d| j         d| j         d           |                    d           d	                    |          S )zwBuild the full bash script that sources snapshot, cd's, runs command,
        re-dumps env vars, and emits CWD markers.'z'\''r   r   zsource z >/dev/null 2>&1 || truezbuiltin cd -- z || exit 126zeval 'z__hermes_ec=$?z{ export -p > z
 && mv -f r   z; } 2>/dev/null || rm -f z 2>/dev/null || truez	pwd -P > z
printf '\nr   z\n' "$(pwd -P)"zexit $__hermes_ec
)
r   r   r   r   r   r   appendr   r   join)	rt   r   r   escapedr   r   r   parts
quoted_cwds	            r   _wrap_commandzBaseEnvironment._wrap_command  s    //#w// {4#677 ;t~66 K 3g =>>K	  	LL@,@@@   ++C00
>j>>>??? 	(g((()))%&&&  	LLH) H Hy H H< H H(1H H H   	G!1GGGHHH
 	R$*RRd.>RRR	
 	
 	
 	()))yyr   c                 b    dt          j                    j        dd          }|  d| d| d| S )z;Append stdin_data as a shell heredoc to the command string.HERMES_STDIN_Nr   z << 'z'
r   )r   r   r   )r   rJ   	delimiters      r   _embed_stdin_heredocz$BaseEnvironment._embed_stdin_heredoc  sG     <DJLL$4SbS$9;;	GG	GGjGGIGGGr   r3   c           
         g  t          j        d          d          fdfd}t          j        |d          }|                                 t          j                    |z   }t          j                    }||d}t          j                    j        }t          d	d
          }	d}
|}d}t                      d
u }t          r/t                              d||	||sdndt                                 	 d}                                !|
dz  }
t                      rt          r8t                              d||	|
t          j                    |d         z
             |                                |                    d           d                              dz   ddS t          j                    |k    rt          rt                              d||	|
|           |                                |                    d           d                              }d| d}|r||z   n|                                ddS t'          |d           t          rt          j                    |z
  dk    rvt                      d
u }t                              d ||	|
t          j                    |d         z
  t                      |sdnd|r|sd!nd           t          j                    }|}t          j        |           |d"k     rt+          |d#z  d"          }                                !n# t,          t.          f$ r~ t          r8t                              d$||	|
t          j                    |d         z
             	 |                                |                    d           n# t0          $ r Y nw xY w w xY w|                    d           	 j                                         n# t0          $ r Y nw xY wt          r>t                              d%||	|
t          j                    |d         z
  j                   d                              j        dS )&u  Poll-based wait with interrupt checking and stdout draining.

        Shared across all backends — not overridden.

        Fires the ``activity_callback`` (if set on this instance) every 10s
        while the process is running so the gateway's inactivity timeout
        doesn't kill long-running commands.

        Also wraps the poll loop in a ``try/finally`` that guarantees we
        call ``self._kill_process(proc)`` if we exit via ``KeyboardInterrupt``
        or ``SystemExit``.  Without this, the local backend (which spawns
        subprocesses with ``os.setsid`` into their own process group) leaves
        an orphan with ``PPID=1`` when python is shut down mid-tool — the
        ``sleep 300``-survives-30-min bug Physikal and I both hit.
        r7   r   r   c                 
   	 | D ]e}|t          |t                    r)                                        |                     C                    t	          |                     fn# t
          $ r Y nw xY w	                     dd          }|r                    |           d S d S # t
          $ r Y d S w xY w# 	                     dd          }|r                    |           w w # t
          $ r Y w w xY wxY w)Nr   Tfinal)r9   bytesr   decoder:   r#   )streampiecetaildecoderoutput_chunkss      r   _drain_iterablez:BaseEnvironment._wait_for_process.<locals>._drain_iterableK  sb   # 9 9E} !%// 9%,,W^^E-B-BCCCC%,,SZZ88889    ">>#T>::D 3%,,T222223 3    DD	">>#T>::D 3%,,T22223    Ds`   A(A, +C  ,
A96C  8A99C  =.B/ /
B=<B= D.C20D2
C?<D>C??Dc                  P   j         } | d S t          | dd           }	 t          |          r
 |            nd }n# t          $ r d }Y nw xY wt	          |t
                    r|dk     r |            d S t          j        dk    r	 	 t          j        |d          }|sn)
	                    	
                    |                     An# t          t          f$ r Y nw xY w	 	
                    dd          }|r
	                    |           nU# t          $ r Y nIw xY w# 	 	
                    dd          }|r
	                    |           w w # t          $ r Y w w xY wxY wd S d}	 	 	 t          j        |gg g d          \  }}}n# t          t          f$ r Y nw xY w|r\	 t          j        |d          }n# t          t          f$ r Y nSw xY w|snL
	                    	
                    |                     d}n                                 |d	z  }|d
k    rn	 	
                    dd          }|r
	                    |           d S d S # t          $ r Y d S w xY w# 	 	
                    dd          }|r
	                    |           w w # t          $ r Y w w xY wxY w)Nfilenor   ntT   r   r   g?r      )rM   r   callabler#   r9   r"   r-   namereadr   r   
ValueErrorr@   selectru   )r   r  fdchunkr   idle_after_exitready_r  r   r  r3   s           r   _drainz1BaseEnvironment._wait_for_process.<locals>._draine  s    [F~VXt44F!)&!1!1;VVXXXt   b#&& "q&&''' w$D "D 1 1$ "!%,,W^^E-B-BCCC	D ""G,   D&~~c~>> 7)00666$   	&~~c~>> 7)0066667$   O"&,mRD"b#&F&Fq!!&0    ""$&GB$5$5EE *G4 " " "!E"$ "!%,,W^^E-B-BCCC*+0 (1,*a//!)"2">>#T>::D 3%,,T222223 3    DD	">>#T>::D 3%,,T22223    Ds   ; A
	A
AC	 D  	CD  CD  !.D 
DD E"".EE"
EE"EE"*I# ,F	 I# 	FI# FI# #F9 8I# 9G
I# GAI#  .I 
I I #J%%.JJ%
J"J%!J""J%TrD   )r   r   pidNr   Fzg[interrupt-debug] _wait_for_process ENTER tid=%s pid=%s timeout=%ss activity_cb=%s initial_interrupt=%sr   MISSINGg{Gzt?r   ut   [interrupt-debug] _wait_for_process INTERRUPT DETECTED tid=%s pid=%s iter=%d elapsed=%.1fs — killing process groupr   rb   r    z
[Command interrupted]   )r   r   zM[interrupt-debug] _wait_for_process TIMEOUT tid=%s pid=%s iter=%d timeout=%ssz
[Command timed out after zs]|   zterminal command runningg      >@zo[interrupt-debug] _wait_for_process HEARTBEAT tid=%s pid=%s iter=%d elapsed=%.0fs interrupt=%s activity_cb=%s%sz (LOST during run)g?g      ?u   [interrupt-debug] _wait_for_process EXCEPTION_EXIT tid=%s pid=%s iter=%d elapsed=%.1fs — killing subprocess group before re-raisezd[interrupt-debug] _wait_for_process EXIT (natural) tid=%s pid=%s iter=%d elapsed=%.1fs returncode=%s)codecsgetincrementaldecoderrF   rG   r   r   r    current_threadidentr   r   _DEBUG_INTERRUPTr   r   r   ru   _kill_processr   lstripr&   sleepminKeyboardInterrupt
SystemExitr#   rM   r>   r   )rt   r3   ry   r  drain_threaddeadline_now_activity_state_tid_pid_iter_count_last_heartbeat_last_interrupt_state_cb_was_none_poll_sleeppartialtimeout_msg_cb_now_noner  r   r  s    `                @@@r   r   z!BaseEnvironment._wait_for_process  sU     $&4 8&.w77yIII	 	 	 	 	 	4F	 F	 F	 F	 F	 F	 F	 F	P !'vdCCC>##g-~
 
 '))/tUD)) %-//47 	KKBdG)8y    V	K))++%q !## ' \ $T^5E5EX_H`5`  
 &&t,,, %%a%000"$''-"8"8;T"T&)   >##h..' @ $W  
 &&t,,, %%a%000 ggm44G"K"K"K"KK ##2'K"7"7(//11&)	   &o7QRRR
 $ 0(8(8?(Jd(R(R#9#;#;t#CLKK8 dK((?7+CC&((%1@y0<Y\Y,,WY	 	 	 '+n&6&6O#/L 
;'''$$"%kC&7"="=K ))++%@ ":. 	 	 	   g$N$$w'??	  ""4(((!!!!,,,,   )	2 	!$$$	K 	 	 	D	  	KKDdK  ?7#;;   ''-00PPPsR   B.L: 0BL: C)L: :AO	+N76O	7
OO	OO	#O= =
P
	P
c                 l    	 |                                  dS # t          t          t          f$ r Y dS w xY w)zDTerminate a process. Subclasses may override for process-group kill.N)rx   ProcessLookupErrorPermissionErrorr@   )rt   r3   s     r   r  zBaseEnvironment._kill_process6  sA    	IIKKKKK"OW= 	 	 	DD	s    33r   c                 0    |                      |           dS )zDExtract CWD from command output. Override for local file-based read.N)_extract_cwd_from_output)rt   r   s     r   r   zBaseEnvironment._update_cwdA  s    %%f-----r   c                 B   |                     dd          }| j        }|                    |          }|dk    rdS t          d|dz
            }|                    |||          }|dk    s||k    rdS ||t	          |          z   |                                         }|r|| _        |                    dd|          }|dk    r|}|                    d|t	          |          z             }	|	dk    r|	dz   nt	          |          }	|d|         ||	d         z   |d<   dS )	zParse the __HERMES_CWD_{session}__ marker from stdout output.

        Updates self.cwd and strips the marker from result["output"].
        Used by remote backends (Docker, SSH, Modal, Daytona, Singularity).
        r   r  Nr   r  r   r   )r!   r   rfindmaxlenstripr   find)
rt   r   r   markerlastsearch_startfirstcwd_path
line_startline_ends
             r   r5  z(BaseEnvironment._extract_cwd_from_outputE  s:    Hb))!||F##2::F 1dTk**V\488B;;%4--F%#f++-45;;== 	 DH \\$511
J;;tTCKK%788#+r>>8a<<s6{{!+:+.		1BBxr   c                     dS )u>  Hook called before each command execution.

        Remote backends (SSH, Modal, Daytona) override this to trigger
        their FileSyncManager.  Bind-mount backends (Docker, Singularity)
        and Local don't need file sync — the host filesystem is directly
        visible inside the container/process.
        Nr   rs   s    r   _before_executezBaseEnvironment._before_executek  s	     	r   r  T)ry   rJ   rewrite_compound_backgroundrF  c                   |                                   |                     |          \  }}|rddlm}  ||          }|p| j        }	|p| j        }
||||z   }n||}n|}|r#| j        dk    r|                     ||          }d}|                     ||
          }| j	         }| 
                    |||	|          }|                     ||	          }|                     |           |S )z=Execute a command, return {"output": str, "returncode": int}.r   )_rewrite_compound_backgroundNheredocr   r   )rE  _prepare_commandtools.terminal_toolrH  ry   r   r   r   r   r   r   r   r   )rt   r   r   ry   rJ   rF  exec_command
sudo_stdinrH  effective_timeouteffective_cwdeffective_stdinwrappedr   r3   r   s                   r   executezBaseEnvironment.executey  sG    	#'#8#8#A#A j ' 	FHHHHHH77EEL#3t|tx !j&<(:5OO#(OO(O  	#t/9<<44\?SSL"O$$\=AA ((~~5*;  
 
 ''6G'HH   r   c                 .    |                                   dS )z.Alias for cleanup (compat with older callers).N)r   rs   s    r   stopzBaseEnvironment.stop  s    r   c                 R    	 |                                   d S # t          $ r Y d S w xY wrr   )r   r#   rs   s    r   __del__zBaseEnvironment.__del__  s:    	LLNNNNN 	 	 	DD	s    
&&c                 $    ddl m}  ||          S )z6Transform sudo commands if SUDO_PASSWORD is available.r   )_transform_sudo_command)rK  rX  )rt   r   rX  s      r   rJ  z BaseEnvironment._prepare_command  s%    ??????&&w///r   rr   )r   r   )r  ) r   r   r   r   r   r:   __annotations__r   r"   r   dictr   boolrp   r   r   r   r   staticmethodr   r   r   r   r  r   r5  rE  rR  rT  rV  r   rJ  r   r   r   r   r   "  s,          K  sc    
% 
%C 
%# 
%D 
% 
% 
% 
%( !%W W WW 	W
 W $JW 
W W W W   ^]) ]) ])F  s  s       \ @ S @ s @ s @  @  @  @ L Hc Hs Hs H H H \HUQ UQm UQc UQD UQ UQ UQ UQn-    .$ . . . . Ct  C  C  C  CL   " .
 #!%,0. . .. .
 t. $J. &*. 
. . . .h    0 0c3:o0F 0 0 0 0 0 0r   r   rr   )8r   r  r]   loggingr-   r  r   rQ   rF   r   r   abcr   r   pathlibr   typingr   r   r   hermes_constantsr	   hermes_cli._subprocess_compatr
   tools.interruptr   	getLoggerr   r   r[  r.   r  setLevelINFOlocalr   r:   r   r   rZ  r&   r2   rR   rH   listrW   r`   rg   r   r   r"   rn   rp   r   r   r   r   r   r   <module>ri     s       				             # # # # # # # #       ) ) ) ) ) ) ) ) ) ) , , , , , , < < < < < < * * * * * *		8	$	$ 4		":;;<<  "
 OOGL!!! +9?,, +hud{3d: +t + + + +
?# 5 < ? ? ? ? 
   4    (9j& 9c 9d 9 9 9 9F .2 	c #d
   .4 D    B4 Bt B B B B Bs uUCZ'84'?    , , , , ,H , , ,$B  B  B  B  B  B  B  B T*C *C * * * *Y
0 Y
0 Y
0 Y
0 Y
0c Y
0 Y
0 Y
0 Y
0 Y
0r   