Ë
    âß²jh"  ã                  ó`  — U d Z ddlm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mZmZ ddlmZ ddlmZ  ej&                  d	«      Z eh d
£«      Z ej&                  d«      ZdZi Zded<    ej6                  «       Zdd„Zdd„Zdd„Zdd„Z dd„Z!dd„Z"edd„«       Z# G d„ d«      Z$y)z]Plugin-owned durable state and namespaced settings helpers (split out of hermes_cli.plugins).é    )ÚannotationsN)Úcontextmanager)ÚPath)ÚAnyÚDictÚMapping)Úget_hermes_home)Ú_portable_skill_namespacez!^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$>   ÚmodelÚpluginsÚsecurityÚsettingsz#^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$i    zDict[str, threading.RLock]Ú_PLUGIN_STATE_LOCKSc                ó  — t        | t        «      st        d«      ‚t        | j	                  d«      «      }|  xs% d| v xs d| v xs |d   j                  «       t        v }|st        d„ |D «       «      st        d«      ‚|S )zãValidate/split a plugin-relative settings key; global paths, traversal, and core roots are rejected
    before any config read.

    The public API accepts only relative keys (``endpoint`` or ``retry.policy``). See #64227.
    z,Expected a plugin-relative config key stringú.ú/ú\r   c              3  óF   K  — | ]  }t         j                  |«      –— Œ y ­w©N)Ú_PLUGIN_SETTING_SEGMENT_REÚ	fullmatch)Ú.0Úsegments     ú=/home/ubuntu/.hermes/hermes-agent/hermes_cli/plugins_state.pyú	<genexpr>z,_plugin_relative_segments.<locals>.<genexpr>"   s   è ø€ Ò`ÈGÔ4×>Ñ>¸w×GÑ`ùs   ‚!zƒExpected a plugin-relative config key such as 'endpoint' or 'retry.policy'; global, cross-plugin, and traversal paths are forbidden)Ú
isinstanceÚstrÚ
ValueErrorÚtupleÚsplitÚlowerÚ_PLUGIN_SETTING_RESERVED_ROOTSÚall)ÚkeyÚsegmentsÚinvalids      r   Ú_plugin_relative_segmentsr'      sˆ   € ô �cœ3ÔÜÐGÓHÐHÜ�S—Y‘Y˜s“^Ó$€HØˆgÒk˜ ˜Òk t¨s {Òk°h¸q±k×6GÑ6GÓ6IÔMkÐ6k€GÙ”cÑ`ÐW_Ô`Ô`ÜðVó
ð 	
ð €Oó    c                óR   — | }|D ]  }t        |t        «      r||vr|c S ||   }Œ! |S )zIWalk ``segments`` through nested mappings; ``default`` on the first miss.)r   r   )Úrootr%   ÚdefaultÚcurrentr   s        r   Ú_nested_plugin_valuer-   *   s=   € à€GØò #ˆÜ˜'¤7Ô+¨w¸gÑ/EØŠNØ˜'Ñ"‰ð#ð €Nr(   c                ó2   — |}t        | «      D ]  }||i}Œ |S )z;Wrap ``value`` in nested single-key dicts, outermost first.)Úreversed)r%   ÚvalueÚnestedr   s       r   Ú_nested_plugin_mappingr2   4   s*   € à€FÜ˜HÓ%ò #ˆØ˜6Ð"‰ð#à€Mr(   c                óJ   — t        | dd|fd«      }t        |t        «      r|S dS )z<``plugins.entries.<plugin_id>`` as a mapping, else ``None``.r   ÚentriesN)r-   r   r   )ÚconfigÚ	plugin_idÚentrys      r   Ú_plugin_settings_entryr8   <   s+   € ä  ¨)°YÀ	Ð)JÈDÓQ€EÜ˜u¤gÔ.ˆ5Ð8°DÐ8r(   c                óà  — ddl m} ddl m} |j                  «       rt	        d«      ‚dd| dg|¢­}dj                  |«      }|j                  |«      rt	        d	|›d
�«      ‚t        |dd t        ||«      «      }t        |j                  «       «      5  |j                  5  |j                  «        |j                  ||hd¬«       ddd«       ddd«       y# 1 sw Y   ŒxY w# 1 sw Y   yxY w)aÆ  Atomically write one value under ``plugins.entries.<plugin_id>.settings``.

    THE writer for plugin settings: ``PluginContext.set_config`` (the plugin itself) and the
    Desktop/TUI ``plugins.manage settings`` action both land here so managed-install and
    administrator-managed-key refusals, the cross-process lock and the fail-closed raw read
    are enforced once. ``segments`` must already be validated by :func:`_plugin_relative_segments`.
    r   )r5   )Úmanaged_scopez6Plugin settings cannot be changed in a managed installr   r4   r   r   zPlugin setting z is administrator-managedNé   T)Úpreserve_keysÚmerge_existing)Ú
hermes_clir5   r:   Ú
is_managedÚPermissionErrorÚjoinÚis_key_managedr2   Ú_locked_plugin_stateÚget_config_pathÚ_CONFIG_LOCKÚread_user_config_rawÚsave_config)r6   r%   r0   Ú
config_modr:   Ú	full_pathÚdotted_pathÚpartials           r   Úsave_plugin_settingrL   B   sô   € õ 0Ý(à×ÑÔÜÐVÓWÐWØ˜I y°*ÐH¸xÑH€IØ—(‘(˜9Ó%€KØ×#Ñ# KÔ0Ü °¨Ð>WÐXÓYÐYÜ$ Y¨r° ]Ô4JÈ8ÐUZÓ4[Ó\€Gô 
˜j×8Ñ8Ó:Ó	;ñ X¸Z×=TÑ=Tñ Xð 	×'Ñ'Ô)Ø×Ñ˜w°y°kÐRVÐÔW÷	X÷ X÷ Xñ Xú÷ Xð Xús$   ÂC$Â!&CÃC$ÃC!	ÃC$Ã$C-c                ó‚   — |xs | }|xr |j                  d«      }|rt        j                  d|«      r|S t        |«      S )z÷Return one Windows-safe directory component for plugin-owned data. Portable Agent Plugins already
    receive this exact PLUGIN_DATA path; otherwise the fixed prefix avoids Windows reserved device names and
    the digest prevents fold collisions.zagent-plugin-z[A-Za-z0-9][A-Za-z0-9_-]{0,191})Ú
startswithÚrer   r
   )r6   Úskill_namespaceÚ	candidateÚportables       r   Ú_plugin_data_namespacerS   ]   sE   € ð  Ò, 9€IØÒH 9×#7Ñ#7¸Ó#H€HÙ”B—L‘LÐ!CÀYÔOØÐÜ$ YÓ/Ð/r(   c           	   #  ó8  K  — | j                  d| j                  › d�«      }t        5  t        j	                  t        |j                  d¬«      «      t        j                  «       «      }ddd«       5  |j                  j                  dd¬«       t        |d«      5 }t        j                  d	k(  r…d
dl}|j                  d
t        j                  «      d
k(  r!|j!                  d«       |j#                  «        |j                  d
«       |j%                  |j'                  «       |j(                  d«       n.d
dl}|j-                  |j'                  «       |j.                  «       	 d–— t        j                  d	k(  r=|j                  d
«       j%                  |j'                  «       |j0                  d«       n+j-                  |j'                  «       |j2                  «       	 ddd«       ddd«       y# 1 sw Y   �ŒŽxY w# t        j                  d	k(  r=|j                  d
«       j%                  |j'                  «       |j0                  d«       w j-                  |j'                  «       |j2                  «       w xY w# 1 sw Y   ŒžxY w# 1 sw Y   yxY w­w)z¨Serialize state read-modify-write across threads/processes (fcntl / msvcrt). The lock lives in a
    sibling file because atomic replacement changes the target's inode.r   z.lockF)ÚstrictNT)ÚparentsÚexist_okza+bÚntr   ó    é   )Ú	with_nameÚnameÚ_PLUGIN_STATE_LOCKS_GUARDr   Ú
setdefaultr   ÚresolveÚ	threadingÚRLockÚparentÚmkdirÚopenÚosÚmsvcrtÚseekÚSEEK_ENDÚwriteÚflushÚlockingÚfilenoÚLK_LOCKÚfcntlÚflockÚLOCK_EXÚLK_UNLCKÚLOCK_UN)ÚpathÚ	lock_pathÚthread_lockÚhandlerf   rn   s         r   rC   rC   h   sä  è ø€ ð —‘  4§9¡9 +¨UÐ3Ó4€IÜ	"ñ nÜ)×4Ñ4´S¸×9JÑ9JÐRWÐ9JÓ9XÓ5YÔ[d×[jÑ[jÓ[lÓmˆ÷nà	ñ @Ø×Ñ×Ñ t°dÐÔ;Ü�)˜UÓ#ð 	@ vÜ�w‰w˜$ŠÛØ—;‘;˜q¤"§+¡+Ó.°!Ò3Ø—L‘L Ô'Ø—L‘L”NØ—‘˜A”Ø—‘˜vŸ}™}›°·±ÀÕBãØ—‘˜FŸM™M›O¨U¯]©]Ô;ð@Ûä—7‘7˜d’?Ø—K‘K ”NØ—N‘N 6§=¡=£?°F·O±OÀQÕGà—K‘K §¡£°·±Õ?÷%	@÷@ð @÷nñ nûô$ —7‘7˜d’?Ø—K‘K ”NØ—N‘N 6§=¡=£?°F·O±OÀQÕGà—K‘K §¡£°·±Õ?ú÷%	@ð 	@ú÷@ð @üsh   ‚&J¨AG6Á*
JÁ4*JÂCJÅ&HÅ*A;JÇ%JÇ-	JÇ6H Ç;JÈA<I?É?JÊJ	ÊJÊJÊJc                  ó|   — e Zd ZdZddd„Zedd„«       Zedd„«       Zedd„«       Ze	dd„«       Z
dd„Zddd	„Zdd
„Zy)ÚPluginStatez?Atomic, quota-bounded JSON key/value state owned by one plugin.c                ó&   — t        ||«      | _        y r   )rS   Ú_data_namespace)Úselfr6   rP   s      r   Ú__init__zPluginState.__init__‰   s   € Ü5°iÀÓQˆÕr(   c                ó6   — t        «       dz  | j                  z  S )z@Profile-scoped directory matching portable plugins' PLUGIN_DATA.zplugin-data)r	   rz   ©r{   s    r   Údata_dirzPluginState.data_dirŒ   s   € ô Ó  =Ñ0°4×3GÑ3GÑGÐGr(   c                ó    — | j                   dz  S )Nz
state.json)r   r~   s    r   rs   zPluginState.path‘   s   € à�}‰}˜|Ñ+Ð+r(   c                ó   — t         S r   )Ú_PLUGIN_STATE_QUOTA_BYTESr~   s    r   Úquota_byteszPluginState.quota_bytes•   s   € ä(Ð(r(   c                ól   — t        | t        «      rt        j                  | «      rd| v rt	        d«      ‚y )Nz..zgPlugin state keys must be 1-128 characters using letters, numbers, '_', '-', '.', or ':' (without '..'))r   r   Ú_PLUGIN_STATE_KEY_REr   r   )r$   s    r   Ú_validate_keyzPluginState._validate_key™   s=   € ä˜#œsÔ#Ô+?×+IÑ+IÈ#Ô+NÐRVÐZ]ÑR]Üð@óð ð S^r(   c                óZ  — 	 t        | j                  d¬«      5 }t        j                  |«      }d d d «       t        t        «      st        d| j                  › d�«      ‚|S # 1 sw Y   Œ4xY w# t        $ r i cY S t
        t        f$ r!}t        d| j                  › d|› �«      |‚d }~ww xY w)Núutf-8)ÚencodingzCannot parse plugin state z: z: root must be an object)
rd   rs   ÚjsonÚloadÚFileNotFoundErrorÚOSErrorr   ÚRuntimeErrorr   Údict)r{   rv   ÚdataÚexcs       r   Ú_read_unlockedzPluginState._read_unlocked¡   s©   € ð	YÜ�d—i‘i¨'Ô2ð )°fÜ—y‘y Ó(�÷)ô ˜$¤Ô%ÜÐ!;¸D¿I¹I¸;ÐF^Ð_Ó`Ð`Øˆ÷)ð )ûä ò 	ØŠIÜœÐ$ò 	YÜÐ!;¸D¿I¹I¸;ÀbÈÈÐNÓOÐUXÐXûð	Yús3   ‚A. ™A"¯A. Á"A+Á'A. Á.B*Á;B*Â	B%Â%B*Nc                óº   — | j                  |«       t        | j                  «      5  | j                  «       j	                  ||«      cddd«       S # 1 sw Y   yxY w)z>Read a JSON value, returning *default* when the key is absent.N)r†   rC   rs   r’   Úget)r{   r$   r+   s      r   r”   zPluginState.get­   sJ   € à×Ñ˜3ÔÜ! $§)¡)Ó,ñ 	;Ø×&Ñ&Ó(×,Ñ,¨S°'Ó:÷	;÷ 	;ò 	;ús   § AÁAc                óò  — | j                  |«       t        | j                  «      5  | j                  «       }|||<   	 t	        j
                  |dd¬«      j                  d«      }t        |«      | j                  kD  r%t        dt        |«      › d	| j                  › d
�«      ‚ddlm}  || j                  |d¬«       ddd«       y# t        t        f$ r}t        d|›d�«      |‚d}~ww xY w# 1 sw Y   yxY w)zBAtomically set one JSON value without dropping concurrent updates.Fé   )Úensure_asciiÚindentrˆ   zPlugin state value for z is not JSON-serializableNzPlugin state quota exceeded: z bytes is greater than the z-byte per-plugin quotar   )Úatomic_json_writei€  )Úmode)r†   rC   rs   r’   rŠ   ÚdumpsÚencodeÚ	TypeErrorr   Úlenrƒ   Úutilsr™   )r{   r$   r0   r�   Úencodedr‘   r™   s          r   ÚsetzPluginState.set³   sÿ   € à×Ñ˜3ÔÜ! $§)¡)Ó,ñ 	;Ø×&Ñ&Ó(ˆDØˆD�‰IðfÜŸ*™* T¸ÀaÔH×OÑOÐPWÓX�ô �7‹|˜d×.Ñ.Ò.Ü Ø3´C¸³L°>ð B Ø $× 0Ñ 0Ð1Ð1GðIóð õ 0Ù˜dŸi™i¨°EÕ:÷	;ð 	;øô
 œzÐ*ò fÜ Ð#:¸3¸'ÐAZÐ![Ó\ÐbeÐeûðfú÷	;ð 	;ús/   §C-¾'CÁ%AC-ÃC*ÃC%Ã%C*Ã*C-Ã-C6)Ú )r6   r   rP   r   ÚreturnÚNone)r£   r   )r£   Úint)r$   r   r£   r¤   )r£   údict[str, Any]r   )r$   r   r+   r   r£   r   )r$   r   r0   r   r£   r¤   )Ú__name__Ú
__module__Ú__qualname__Ú__doc__r|   Úpropertyr   rs   rƒ   Ústaticmethodr†   r’   r”   r¡   © r(   r   rx   rx   †   sl   „ ÙIôRð òHó ðHð ò,ó ð,ð ò)ó ð)ð òó ðó
ô;ô;r(   rx   )r$   r   r£   útuple[str, ...])r*   Úobjectr%   r®   r+   r   r£   r   )r%   r®   r0   r   r£   r¦   )r5   r¯   r6   r   r£   zMapping[str, Any] | None)r6   r   r%   r®   r0   r   r£   r¤   )r6   r   rP   r   r£   r   )rs   r   )%rª   Ú
__future__r   rŠ   re   rO   r`   Ú
contextlibr   Úpathlibr   Útypingr   r   r   Úhermes_constantsr	   Úhermes_cli.plugins_manifestr
   Úcompiler   Ú	frozensetr"   r…   r‚   r   Ú__annotations__ÚLockr]   r'   r-   r2   r8   rL   rS   rC   rx   r­   r(   r   ú<module>rº      s¹   ðÚ cå "ã Û 	Û 	Û Ý %Ý ß %Ñ %å ,Ý Aà'˜RŸZ™ZÐ(LÓMÐ Ù!*Ò+WÓ!XÐ Ø!�r—z‘zÐ"HÓIÐ Ø,Ð Ø24Ð Ð/Ó 4Ø*˜IŸN™NÓ,Ð óó$óó9óXó60ð ò@ó ð@÷:=;ò =;r(   