
    iy                        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  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
e.e/e0f         dz  fdZ1 G d de          Z2 G d d          Z3d e"d
e"fd!Z4 G d" d#e          Z5d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)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/ubuntu/.hermes/hermes-agent/tools/environments/base.pyset_activity_callbackr   .   s    (*%%%    c                  .    t          t          dd           S )Nr   )getattrr    r   r   _get_activity_callbackr   3   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%   7   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_dirr1   Q   sT     Y-..F ,LL+GGD4G(((Hr   procdatac                 f      fd}t          j        |d                                           dS )zMWrite *data* to proc.stdin on a daemon thread to avoid pipe-buffer deadlocks.c                      	 j                                         j                                          d S # t          t          f$ r Y d S w xY wN)stdinwritecloseBrokenPipeErrorOSError)r3   r2   s   r   _writez_pipe_stdin.<locals>._writeh   s_    	JT"""J) 	 	 	DD	s   38 AATtargetdaemonN)	threadingThreadr   )r2   r3   r<   s   `` r   _pipe_stdinrB   e   sL          F40006688888r   cmd
stdin_datac                     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.
    NT)stdoutstderrr7   text)
subprocessPopenPIPESTDOUTDEVNULLrB   )rC   rD   kwargsr2   s       r   _popen_bashrO   r   si      !+!7jooZ=O   D D*%%%Kr   pathc                     |                                  r7	 t          j        |                                           S # t          $ r Y nw xY wi S )z:Load a JSON file as a dict, returning ``{}`` on any error.)existsjsonloads	read_textr"   )rP   s    r   _load_json_storerV      sV    {{}} 	:dnn../// 	 	 	D	Is   %< 
A	A	c                     | j                             dd           |                     t          j        |d                     dS )z.Write *data* as pretty-printed JSON to *path*.Tr)      )indentN)parentr.   
write_textrS   dumps)rP   r3   s     r   _save_json_storer]      sD    KdT222OODJtA.../////r   	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;   )r^   sts     r   _file_mtime_keyrd      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 r6   r   selfs    r   pollzProcessHandle.poll         r   c                     d S r6   r   rh   s    r   killzProcessHandle.kill   rk   r   timeoutc                     d S r6   r   ri   rn   s     r   waitzProcessHandle.wait   rk   r   c                     d S r6   r   rh   s    r   rF   zProcessHandle.stdout       (+r   c                     d S r6   r   rh   s    r   
returncodezProcessHandle.returncode   rs   r   r   Nr6   )__name__
__module____qualname____doc__r!   rj   rm   floatrq   propertyr   strrF   ru   r   r   r   rf   rf      s          &cDj%%%%<<EDL<C<<<<+3$+++ X++C$J+++ X+++r   rf   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rutf-8replace)encoding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)Nr   r   r      )_returncoder,   r8   	_write_fdencoder;   r"   _errorr9   _doneset)output	exit_codeexcr   ri   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
Tr=   )
_cancel_fnr@   Eventr   r   r   r,   pipefdopen_stdoutr   rA   r   )ri   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 r6   )r   rh   s    r   rF   z_ThreadedProcessHandle.stdout   s
    |r   r   c                     | j         S r6   )r   rh   s    r   ru   z!_ThreadedProcessHandle.returncode   s    r   c                 F    | j                                         r| j        nd S r6   )r   is_setr   rh   s    r   rj   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 r6   )r   r"   rh   s    r   rm   z_ThreadedProcessHandle.kill   sS    ? 	!!!!!   	 	s    
--rn   c                 F    | j                             |           | j        S )Nrn   )r   rq   r   rp   s     r   rq   z_ThreadedProcessHandle.wait   s!    
(((r   r6   )rw   rx   ry   rz   r   tupler}   r!   r   r|   rF   ru   rj   rm   r{   rq   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ed	ed
edz  dedz  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   rh   s    r   get_temp_dirzBaseEnvironment.get_temp_dir  s	     vr   Ncwdrn   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   rn   r   uuiduuid4hex_session_idr   rstrip_snapshot_path	_cwd_filer   _snapshot_ready)ri   r   rn   r   temp_dirs        r   r   zBaseEnvironment.__init__"  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   loginrn   rD   
cmd_stringr   rD   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typerw   )ri   r   r   rn   rD   s        r   	_run_bashzBaseEnvironment._run_bash2  s$     "T$ZZ%8"U"U"UVVVr   c                     dS )z<Release backend resources (container, instance, connection).Nr   rh   s    r   cleanupzBaseEnvironment.cleanupA  s	     	r   c                 L   t          j        | j                  }d| j         d| j         d| j         d| j         d| j         d| j         d| d| j         d	| j         d
| j         d}	 |                     |d| j                  }|                     || j                  }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``.
        export -p > z#
declare -f | grep -vE '^_[^_]' >> z
alias -p >> z#
echo 'shopt -s expand_aliases' >> z
echo 'set +e' >> z
echo 'set +u' >> z
builtin cd z 2>/dev/null || true
pwd -P > z 2>/dev/null || true
printf '\n%sz\n' "$(pwd -P)"
T)r   rn   r   z-Session snapshot created (session=%s, cwd=%s)uL   init_session failed (session=%s): %s — falling back to bash -l per commandFN)shlexquoter   r   r   r   r   r   _wait_for_processr   _update_cwdloggerinfor   r"   warning)ri   _quoted_cwd	bootstrapr2   resultr   s         r   init_sessionzBaseEnvironment.init_sessionJ  s    k$(++U4. U U151DU U.U U 261DU U !% 3	U U
 !% 3U U &U U U U *U U /3.>U U U 		)>>)4AW>XXD++D$:P+QQF#'D V$$$KK?     
  	) 	) 	)NN6 	   $)D       	)s   )A;C& &
D#0(DD#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/rX   N)
startswithr   r   )r   s    r   _quote_cwd_for_cdz!BaseEnvironment._quote_cwd_for_cdx  sd     #::J$;;7>>$ 	32EKABB00222{3r   commandc                 Z   |                     dd          }g }| j        r|                    d| j         d           |                     |          }|                    d| d           |                    d| d           |                    d           | j        r|                    d	| j         d
           |                    d| j         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'\''zsource z >/dev/null 2>&1 || truezbuiltin cd z || exit 126zeval 'z__hermes_ec=$?r   z 2>/dev/null || truez	pwd -P > z
printf '\nr   z\n' "$(pwd -P)"zexit $__hermes_ec
)r   r   appendr   r   r   r   join)ri   r   r   escapedparts
quoted_cwds         r   _wrap_commandzBaseEnvironment._wrap_command  sb    //#w//  	LLG$-GGG   ++C00
;:;;;<<< 	(g((()))%&&&  	SLLQ(;QQQRRR 	EEEEFFF
 	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   rD   	delimiters      r   _embed_stdin_heredocz$BaseEnvironment._embed_stdin_heredoc  sG     <DJLL$4SbS$9;;	GG	GGjGGIGGGr   r2   c           
         g  t          j        d          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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                                            n# t*          t,          f$ r~ t          r8t                              d!||	|
t          j                    |d         z
             	 |                                |                    d           n# t.          $ r Y nw xY w w xY w|                    d           	 j                                         n# t.          $ 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.
        r   r   r   c                     j                                         } 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)	Nr   Tg?   r      r   )final)rF   filenoselect
ValueErrorr;   r,   readr   decoderj   r"   )	fdidle_after_exitready_chunktaildecoderoutput_chunksr2   s	         r   _drainz1BaseEnvironment._wait_for_process.<locals>._drain  s   ##%%B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   D = D AD AD A- ,D -B>D  BAD .D 
DDE.E	E	
EEEETr=   )r   r   pidNr   Fzg[interrupt-debug] _wait_for_process ENTER tid=%s pid=%s timeout=%ss activity_cb=%s initial_interrupt=%sr   MISSINGr   ut   [interrupt-debug] _wait_for_process INTERRUPT DETECTED tid=%s pid=%s iter=%d elapsed=%.1fs — killing process groupr   rX   r    z
[Command interrupted]   )r   ru   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?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getincrementaldecoderr@   rA   r   r   r   current_threadidentr   r   _DEBUG_INTERRUPTr   r   r
   rj   _kill_processr   lstripr%   sleepKeyboardInterrupt
SystemExitr"   rF   r9   ru   )ri   r2   rn   r   drain_threaddeadline_now_activity_state_tid_pid_iter_count_last_heartbeat_last_interrupt_state_cb_was_nonepartialtimeout_msg_cb_now_noner   r   s    `               @@r   r   z!BaseEnvironment._wait_for_process  s      $&4 8&.w77yIII"	 "	 "	 "	 "	 "	 "	H !'vdCCC>##g-~
 
 '))/tUD)) %-//47 	KKBdG)8y    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
3k ))++%l ":. 	 	 	   g$N$$w'??	  ""4(((!!!!,,,,   )	2 	!$$$	K 	 	 	D	  	KKDdK  ?7#;;   ''-00PPPsR   9B,L &BL CL AN&(+NN&
N!N& N!!N& O 
O'&O'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)rm   ProcessLookupErrorPermissionErrorr;   )ri   r2   s     r   r  zBaseEnvironment._kill_process  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)ri   r   s     r   r   zBaseEnvironment._update_cwd  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)
ri   r   r   markerlastsearch_startfirstcwd_path
line_startline_ends
             r   r  z(BaseEnvironment._extract_cwd_from_output  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   rh   s    r   _before_executezBaseEnvironment._before_execute  s	     	r   r   )rn   rD   c                   |                                   |                     |          \  }}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   )r-  _prepare_commandtools.terminal_toolr/  rn   r   r   r   r   r   r   r   r   )ri   r   r   rn   rD   exec_command
sudo_stdinr/  effective_timeouteffective_cwdeffective_stdinwrappedr   r2   r   s                  r   executezBaseEnvironment.execute  s?    	#'#8#8#A#A j 	EDDDDD33LAA#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   rh   s    r   stopzBaseEnvironment.stop  s    r   c                 R    	 |                                   d S # t          $ r Y d S w xY wr6   )r   r"   rh   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)r2  r?  )ri   r   r?  s      r   r1  z BaseEnvironment._prepare_command  s%    ??????&&w///r   r6   )r   rv   )r   ) rw   rx   ry   rz   r   r}   __annotations__r   r!   r   dictr   boolrf   r   r   r   r   staticmethodr   r   r   r   r  r   r  r-  r9  r;  r=  r   r1  r   r   r   r   r     s          K  sc    
% 
%C 
%# 
%D 
% 
% 
% 
%( !%W W WW 	W
 W $JW 
W W W W   ^() () ()\  s  s       \ * S * s * s *  *  *  * ` Hc Hs Hs H H H \HLQ LQm LQc LQD LQ LQ LQ LQ\-    .$ . . . . Ct  C  C  C  CL   " .
 #!%. . .. .
 t. $J. 
. . . .h    0 0c3:o0F 0 0 0 0 0 0r   r   r6   )6rz   r  rS   loggingr,   r   r   rI   r@   r   r   abcr   r   pathlibr   typingr   r   r   hermes_constantsr	   tools.interruptr
   	getLoggerrw   r   rB  r-   r  setLevelINFOlocalr   r}   r   r   rA  r%   r1   rJ   rB   listrO   rV   r]   r   r{   r!   rd   rf   r   r   r   r   r   r   <module>rO     s       				             # # # # # # # #       ) ) ) ) ) ) ) ) ) ) , , , , , , * * * * * *		8	$	$ 4		":;;<<  "
 OOGL!!! +9?,, +hud{3d: +t + + + +
?# 5 < ? ? ? ? 
   4    (
9j& 
9c 
9d 
9 
9 
9 
9 .2 	c #d
   ,4 D    04 0t 0 0 0 0 0s uUCZ'84'?    , , , , ,H , , ,$B  B  B  B  B  B  B  B T*C *C * * * *E0 E0 E0 E0 E0c E0 E0 E0 E0 E0r   