§
    ‰eîiŒ$  ã                   óL  — 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mZ ddl	m
Z
 ddlmZ ddlmZmZmZmZ  ej        e¦  «        Z e¦   «         dz  Zdefd	„Zdefd
„Zdefd„Zdeddfd„Zdefd„Zdefd„Z ej        ¦   «         Zddededefd„Z  G d„ de¦  «        Z!dS )a  Singularity/Apptainer persistent container environment.

Security-hardened with --containall, --no-home, capability dropping.
Supports configurable resource limits and optional filesystem persistence
via writable overlay directories that survive across sessions.
é    N)ÚPath)ÚOptional)Úget_hermes_home)ÚBaseEnvironmentÚ_load_json_storeÚ_popen_bashÚ_save_json_storezsingularity_snapshots.jsonÚreturnc                  óx   — t          j        d¦  «        rdS t          j        d¦  «        rdS t          d¦  «        ‚)z/Locate the apptainer or singularity CLI binary.Ú	apptainerÚsingularityz´Neither 'apptainer' nor 'singularity' was found in PATH. Install Apptainer (https://apptainer.org/docs/admin/main/installation.html) or Singularity and ensure the CLI is available.)ÚshutilÚwhichÚRuntimeError© ó    ú@/home/div/.hermes/hermes-agent/tools/environments/singularity.pyÚ_find_singularity_executabler      sG   € å„|�KÑ Ô ð Øˆ{Ý„|�MÑ"Ô"ð Øˆ}Ý
ð	:ñô ð r   c                  óp  — t          ¦   «         } 	 t          j        | dgddd¬¦  «        }nB# t          $ r t	          d| › d�¦  «        ‚t          j        $ r t	          d| › d�¦  «        ‚w xY w|j        d	k    r>|j                             ¦   «         d
d…         }t	          d| › d|j        › d|› �¦  «        ‚| S )z?Preflight check: resolve the executable and verify it responds.ÚversionTé
   ©Úcapture_outputÚtextÚtimeoutz"Singularity backend selected but 'z' could not be executed.ú'z version' timed out.r   NéÈ   z version' failed (exit code z): )	r   Ú
subprocessÚrunÚFileNotFoundErrorr   ÚTimeoutExpiredÚ
returncodeÚstderrÚstrip)ÚexeÚresultr#   s      r   Ú_ensure_singularity_availabler'   +   s  € å
&Ñ
(Ô
(€Cð	:Ý”Ø�)Ð¨T¸Àbð
ñ 
ô 
ˆˆøõ ð 
ð 
ð 
ÝØN°ÐNÐNÐNñ
ô 
ð 	
õ Ô$ð :ð :ð :ÝÐ8˜sÐ8Ð8Ð8Ñ9Ô9Ð9ð:øøøð Ô˜AÒÐØ”×$Ò$Ñ&Ô& t¨ tÔ,ˆÝÐ^˜sÐ^Ð^ÀÔ@QÐ^Ð^ÐV\Ð^Ð^Ñ_Ô_Ð_Ø€Js	   �+ «?A*c                  ó*   — t          t          ¦  «        S ©N)r   Ú_SNAPSHOT_STOREr   r   r   Ú_load_snapshotsr+   ?   s   € Ý�OÑ,Ô,Ð,r   Údatac                 ó0   — t          t          | ¦  «         d S r)   )r	   r*   )r,   s    r   Ú_save_snapshotsr.   C   s   € Ý•_ dÑ+Ô+Ð+Ð+Ð+r   c                  óø  — t          j        d¦  «        } | r(t          | ¦  «        }|                     dd¬¦  «         |S ddlm}  |¦   «         dz  }t          d¦  «        }|                     ¦   «         rnt          j        |t           j        ¦  «        rO|t          j        dd	¦  «        z  d
z  }|                     dd¬¦  «         t           
                    d|¦  «         |S |                     dd¬¦  «         |S )NÚTERMINAL_SCRATCH_DIRT©ÚparentsÚexist_okr   )Úget_sandbox_dirr   z/scratchÚUSERÚhermeszhermes-agentz Using /scratch for sandboxes: %s)ÚosÚgetenvr   ÚmkdirÚtools.environments.baser4   ÚexistsÚaccessÚW_OKÚloggerÚinfo)Úcustom_scratchÚscratch_pathr4   ÚsandboxÚscratchÚuser_scratchs         r   Ú_get_scratch_dirrE   G   s  € Ý”YÐ5Ñ6Ô6€NØð Ý˜NÑ+Ô+ˆØ×Ò 4°$ÐÑ7Ô7Ð7ØÐà7Ð7Ð7Ð7Ð7Ð7ØˆoÑÔ -Ñ/€Gå�:ÑÔ€GØ‡~‚~ÑÔð �BœI g­r¬wÑ7Ô7ð Ø¥¤¨6°8Ñ!<Ô!<Ñ<¸~ÑMˆØ×Ò 4°$ÐÑ7Ô7Ð7Ý�ŠÐ6¸ÑEÔEÐEØÐà‡M‚M˜$¨€MÑ.Ô.Ð.Ø€Nr   c                  óÖ   — t          j        d¦  «        } | r(t          | ¦  «        }|                     dd¬¦  «         |S t	          ¦   «         }|dz  }|                     dd¬¦  «         |S )NÚAPPTAINER_CACHEDIRTr1   z
.apptainer)r7   r8   r   r9   rE   )Ú	cache_dirÚ
cache_pathrC   s      r   Ú_get_apptainer_cache_dirrJ   \   sx   € Ý”	Ð.Ñ/Ô/€IØð Ý˜)‘_”_ˆ
Ø×Ò °ÐÑ5Ô5Ð5ØÐÝÑ Ô €GØ˜<Ñ'€JØ×Ò˜T¨DÐÑ1Ô1Ð1ØÐr   r   ÚimageÚ
executablec                 ó(  — |                       d¦  «        r#t          | ¦  «                             ¦   «         r| S |                      d¦  «        s| S |                      dd¦  «                             dd¦  «                             dd¦  «        }t          ¦   «         }||› d�z  }|                     ¦   «         rt          |¦  «        S t          5  |                     ¦   «         rt          |¦  «        cd d d ¦  «         S t           	                    d¦  «         t           	                    d| ¦  «         t           	                    d	|¦  «         |d
z  }| 
                    dd¬¦  «         t          j                             ¦   «         }t          |¦  «        |d<   t          |¦  «        |d<   	 t          j        |dt          |¦  «        | gddd|¬¦  «        }|j        dk    rPt                               d¦  «         t                               d|j        d d…         ¦  «         | cd d d ¦  «         S t           	                    d¦  «         t          |¦  «        cd d d ¦  «         S # t          j        $ rS t                               d¦  «         |                     ¦   «         r|                     ¦   «          | cY cd d d ¦  «         S t*          $ r3}t                               d|¦  «         | cY d }~cd d d ¦  «         S d }~ww xY w# 1 swxY w Y   d S )Nz.sifz	docker://Ú ú/Ú-ú:z&Building SIF image (one-time setup)...z  Source: %sz  Target: %sÚtmpTr1   ÚAPPTAINER_TMPDIRrG   ÚbuildiX  )r   r   r   Úenvr   z/SIF build failed, falling back to docker:// URLz  Error: %siô  zSIF image built successfullyz2SIF build timed out, falling back to docker:// URLz2SIF build error: %s, falling back to docker:// URL)Úendswithr   r;   Ú
startswithÚreplacerJ   ÚstrÚ_sif_build_lockr>   r?   r9   r7   ÚenvironÚcopyr   r   r"   Úwarningr#   r!   ÚunlinkÚ	Exception)	rK   rL   Ú
image_namerH   Úsif_pathÚtmp_dirrU   r&   Úes	            r   Ú_get_or_build_sifrd   k   s¤  € Ø‡~‚~�fÑÔð ¥$ u¡+¤+×"4Ò"4Ñ"6Ô"6ð ØˆØ×Ò˜KÑ(Ô(ð Øˆà—’˜{¨BÑ/Ô/×7Ò7¸¸SÑAÔA×IÒIÈ#ÈsÑSÔS€JÝ(Ñ*Ô*€IØ˜jÐ.Ð.Ð.Ñ.€Hà‡‚ÑÔð Ý�8‰}Œ}Ðå	ð !ð !Ø�?Š?ÑÔð 	!Ý�x‘=”=ð!ð !ð !ð !ñ !ô !ð !ð !õ 	�ŠÐ<Ñ=Ô=Ð=Ý�Š�N EÑ*Ô*Ð*Ý�Š�N HÑ-Ô-Ð-à˜eÑ#ˆØ�Š˜d¨TˆÑ2Ô2Ð2åŒj�oŠoÑÔˆÝ"% g¡,¤,ˆÐÑÝ$'¨	¡N¤NˆÐ Ñ!ð	Ý”^Ø˜W¥c¨(¡m¤m°UÐ;Ø#¨$¸Àðñ ô ˆFð Ô  AÒ%Ð%Ý—’ÐPÑQÔQÐQÝ—’˜}¨f¬m¸D¸S¸DÔ.AÑBÔBÐBØð/!ð !ð !ð !ñ !ô !ð !ð !õ0 �KŠKÐ6Ñ7Ô7Ð7Ý�x‘=”=ð3!ð !ð !ð !ñ !ô !ð !ð !øõ4 Ô(ð 	ð 	ð 	Ý�NŠNÐOÑPÔPÐPØ�ŠÑ Ô ð "Ø—’Ñ!Ô!Ð!ØˆLˆLð=!ð !ð !ð !ñ !ô !ð !ð !õ> ð 	ð 	ð 	Ý�NŠNÐOÐQRÑSÔSÐSØˆLˆLˆLˆLˆLðC!ð !ð !ð !ñ !ô !ð !ð !øøøøð>	øøøð?!ð !ð !ð !øøøð !ð !ð !ð !ð !ð !sW   Ã#LÃ>B.LÆ-A8I'È2(I'É'ALÊ:LË	LËK?Ë-LË.LË?LÌLÌLÌLc                   ó–   ‡ — e Zd ZdZ	 	 	 	 	 	 	 ddeded	ed
ededededefˆ fd„Zd„ Z	ddddœdeded	ededz  de
j        f
d„Zd„ Zˆ xZS )ÚSingularityEnvironmenta  Hardened Singularity/Apptainer container with resource limits and persistence.

    Spawn-per-call: every execute() spawns a fresh ``apptainer exec ... bash -c`` process.
    Session snapshot preserves env vars across calls.
    CWD persists via in-band stdout markers.
    Ú~é<   r   FÚdefaultrK   Úcwdr   ÚcpuÚmemoryÚdiskÚpersistent_filesystemÚtask_idc	                 óL  •— t          ¦   «                              ||¬¦  «         t          ¦   «         | _        t	          || j        ¦  «        | _        dt          j        ¦   «         j        d d…         › �| _	        d| _
        || _        || _        d | _        || _        || _        | j        rQt!          ¦   «         dz  }	|	                     dd¬¦  «         |	d|› �z  | _        | j                             dd¬¦  «         |                      ¦   «          |                      ¦   «          d S )	N)rj   r   Úhermes_é   Fzhermes-overlaysTr1   zoverlay-)ÚsuperÚ__init__r'   rL   rd   rK   ÚuuidÚuuid4ÚhexÚinstance_idÚ_instance_startedÚ_persistentÚ_task_idÚ_overlay_dirÚ_cpuÚ_memoryrE   r9   Ú_start_instanceÚinit_session)ÚselfrK   rj   r   rk   rl   rm   rn   ro   Úoverlay_baseÚ	__class__s             €r   rt   zSingularityEnvironment.__init__¤   s#  ø€ õ 	‰Œ×Ò˜S¨'ÐÑ2Ô2Ð2Ý7Ñ9Ô9ˆŒÝ& u¨d¬oÑ>Ô>ˆŒ
Ø<¥T¤Z¡\¤\Ô%5°c°r°cÔ%:Ð<Ð<ˆÔØ!&ˆÔØ0ˆÔØˆŒØ,0ˆÔØˆŒ	ØˆŒàÔð 	AÝ+Ñ-Ô-Ð0AÑAˆLØ×Ò t°dÐÑ;Ô;Ð;Ø ,Ð/C¸'Ð/CÐ/CÑ CˆDÔØÔ×#Ò#¨D¸4Ð#Ñ@Ô@Ð@à×ÒÑÔÐØ×ÒÑÔÐÐÐr   c                 ó^  — | j         ddg}|                     ddg¦  «         | j        r1| j        r*|                     dt	          | j        ¦  «        g¦  «         n|                     d¦  «         	 ddlm}m}  |¦   «         D ]+}|                     d	|d
         › d|d         › d�g¦  «         Œ, |¦   «         D ]+}|                     d	|d
         › d|d         › d�g¦  «         Œ,n2# t          $ r%}t                               d|¦  «         Y d }~nd }~ww xY w| j        dk    r|                     d| j        › d�g¦  «         | j        dk    r)|                     dt	          | j        ¦  «        g¦  «         |                     t	          | j        ¦  «        | j        g¦  «         	 t!          j        |ddd¬¦  «        }|j        dk    rt'          d|j        › �¦  «        ‚d| _        t                               d| j        | j        ¦  «         d S # t           j        $ r t'          d¦  «        ‚w xY w)NÚinstanceÚstartz--containallz	--no-homez	--overlayz--writable-tmpfsr   )Úget_credential_file_mountsÚget_skills_directory_mountz--bindÚ	host_pathrQ   Úcontainer_pathz:roz8Singularity: could not load credential/skills mounts: %sz--memoryÚMz--cpusTéx   r   zFailed to start instance: z/Singularity instance %s started (persistent=%s)zInstance start timed out)rL   Úextendrz   r|   rY   ÚappendÚtools.credential_filesr‡   rˆ   r_   r>   Údebugr~   r}   rK   rx   r   r   r"   r   r#   ry   r?   r!   )r�   Úcmdr‡   rˆ   Úmount_entryÚskills_mountrc   r&   s           r   r   z&SingularityEnvironment._start_instanceÃ   s¥  € ØŒ 
¨GÐ4ˆØ�
Š
�N KÐ0Ñ1Ô1Ð1àÔð 	+ Ô 1ð 	+Ø�JŠJ˜¥S¨Ô):Ñ%;Ô%;Ð<Ñ=Ô=Ð=Ð=à�JŠJÐ)Ñ*Ô*Ð*ð	XØeÐeÐeÐeÐeÐeÐeÐeØ9Ð9Ñ;Ô;ð ið i�Ø—
’
˜H¨°[Ô)AÐ&fÐ&fÀKÐP`ÔDaÐ&fÐ&fÐ&fÐgÑhÔhÐhÐhØ :Ð :Ñ <Ô <ð kð k�Ø—
’
˜H¨°kÔ)BÐ&hÐ&hÀ\ÐRbÔEcÐ&hÐ&hÐ&hÐiÑjÔjÐjÐjðkøåð 	Xð 	Xð 	XÝ�LŠLÐSÐUVÑWÔWÐWÐWÐWÐWÐWÐWøøøøð	Xøøøð Œ<˜!ÒÐØ�JŠJ˜
 t¤|Ð$6Ð$6Ð$6Ð7Ñ8Ô8Ð8ØŒ9�qŠ=ˆ=Ø�JŠJ˜¥# d¤i¡.¤.Ð1Ñ2Ô2Ð2à�
Š
•C˜œ
‘O”O TÔ%5Ð6Ñ7Ô7Ð7ð	;Ý”^ C¸À4ÐQTÐUÑUÔUˆFØÔ  AÒ%Ð%Ý"Ð#OÀÄÐ#OÐ#OÑPÔPÐPØ%)ˆDÔ"Ý�KŠKÐIØÔ(¨$Ô*:ñ<ô <ð <ð <ð <øåÔ(ð 	;ð 	;ð 	;ÝÐ9Ñ:Ô:Ð:ð	;øøøs&   Á0A4C% Ã%
DÃ/DÄDÆ$A'H ÈH,rŒ   N)Úloginr   Ú
stdin_dataÚ
cmd_stringr”   r•   r
   c                óÚ   — | j         st          d¦  «        ‚| j        dd| j        › �g}|r|                     ddd|g¦  «         n|                     dd|g¦  «         t          ||¦  «        S )z5Spawn a bash process inside the Singularity instance.z Singularity instance not startedÚexeczinstance://Úbashz-lz-c)ry   r   rL   rx   r�   r   )r�   r–   r”   r   r•   r‘   s         r   Ú	_run_bashz SingularityEnvironment._run_bashæ   sŠ   € ð Ô%ð 	CÝÐAÑBÔBÐBàŒ Ø/˜TÔ-Ð/Ð/ð1ˆàð 	3Ø�JŠJ˜  d¨JÐ7Ñ8Ô8Ð8Ð8à�JŠJ˜  jÐ1Ñ2Ô2Ð2å˜3 
Ñ+Ô+Ð+r   c                 ó¸  — | j         r‡	 t          j        | j        dd| j        gddd¬¦  «         t
                               d| j        ¦  «         n8# t          $ r+}t
                               d| j        |¦  «         Y d}~nd}~ww xY wd	| _         | j	        rB| j
        r=t          ¦   «         }t          | j
        ¦  «        || j        <   t          |¦  «         dS dS dS )
z;Stop the instance. If persistent, the overlay dir survives.r…   ÚstopTé   r   zSingularity instance %s stoppedz*Failed to stop Singularity instance %s: %sNF)ry   r   r   rL   rx   r>   r?   r_   r]   rz   r|   r+   rY   r{   r.   )r�   rc   Ú	snapshotss      r   ÚcleanupzSingularityEnvironment.cleanupö   s  € àÔ!ð 		+ðbÝ”Ø”_ j°&¸$Ô:JÐKØ#'¨d¸Bðñ ô ð õ —’Ð=¸tÔ?OÑPÔPÐPÐPøÝð bð bð bÝ—’ÐKÈTÔM]Ð_`ÑaÔaÐaÐaÐaÐaÐaÐaøøøøðbøøøà%*ˆDÔ"àÔð 	' Ô 1ð 	'Ý'Ñ)Ô)ˆIÝ'*¨4Ô+<Ñ'=Ô'=ˆI�d”mÑ$Ý˜IÑ&Ô&Ð&Ð&Ð&ð	'ð 	'ð 	'ð 	's   ‰AA Á
BÁ!B Â B)rg   rh   r   r   r   Fri   )Ú__name__Ú
__module__Ú__qualname__Ú__doc__rY   ÚintÚfloatÚboolrt   r   r   ÚPopenrš   rŸ   Ú__classcell__)rƒ   s   @r   rf   rf   œ   s'  ø€ € € € € ðð ð ØØØØØ&+Ø ðð àðð ðð ð	ð
 ðð ðð ðð  $ðð ðð ð ð ð ð ð>!;ð !;ð !;ðF ;@Ø!$Ø+/ð,ð ,ð , Cð ,°4ð ,Øð,à! D™jð,à4>Ô4Dð,ð ,ð ,ð ,ð 'ð 'ð 'ð 'ð 'ð 'ð 'r   rf   )r   )"r£   Úloggingr7   r   r   Ú	threadingru   Úpathlibr   Útypingr   Úhermes_constantsr   r:   r   r   r   r	   Ú	getLoggerr    r>   r*   rY   r   r'   Údictr+   r.   rE   rJ   ÚLockrZ   rd   rf   r   r   r   ú<module>r±      s  ððð ð €€€Ø 	€	€	€	Ø €€€Ø Ð Ð Ð Ø Ð Ð Ð Ø €€€Ø Ð Ð Ð Ð Ð Ø Ð Ð Ð Ð Ð à ,Ð ,Ð ,Ð ,Ð ,Ð ,ðð ð ð ð ð ð ð ð ð ð ð ð 
ˆÔ	˜8Ñ	$Ô	$€à!�/Ñ#Ô#Ð&BÑB€ð
 cð 
ð 
ð 
ð 
ð sð ð ð ð ð(-˜ð -ð -ð -ð -ð,˜$ð , 4ð ,ð ,ð ,ð ,ð˜$ð ð ð ð ð*	 $ð 	ð 	ð 	ð 	ð !�)”.Ñ"Ô"€ð.ð .˜Sð .¨cð .ÀCð .ð .ð .ð .ðbj'ð j'ð j'ð j'ð j'˜_ñ j'ô j'ð j'ð j'ð j'r   