
    4Rjm+                       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ZddlZddl	m
Z
 ddlmZmZ ddlmZ ddlZddlmZ e
rdd	lmZ d
gZdZ ej.                  d      ZddZddZddZddZddZddZddZ	 	 	 	 	 	 	 	 ddZ ddZ!y)zSkill download + archive extraction for the agent toolset.

Split out from ``agent_toolset`` because fetching a session agent's skills and
safely unpacking a (possibly third-party) archive is a distinct concern from the
tool implementations themselves.
    )annotationsN)TYPE_CHECKING)PathPurePosixPath)partial)run_sync   )AsyncAnthropicdownload_session_skillsi  z!anthropic.lib.tools.agent_toolsetc                F    	 | j                  |       y# t        $ r Y yw xY w)zJTrue if ``child`` is ``root`` or a path inside it (both already resolved).FT)relative_to
ValueError)childroots     c/var/www/html/meridian/meridian-ai/venv/lib/python3.12/site-packages/anthropic/lib/tools/_skills.py_withinr   #   s.    $   s    	  c                Z   | j                  dd      }|j                  d      st        |      j                         rt	        d|       t        |      j
                  D cg c]
  }|dk7  s	| }}t        d |D              rt	        d|       |rt        t        |       S dS c c}w )u@  Return ``name`` as a confined relative path, or raise on path-traversal.

    Strips ``.`` components; rejects absolute paths and any ``..`` component
    outright (those only appear in malicious archives). Returns ``""`` for
    entries that resolve to nothing (e.g. ``"./"``) — the caller should skip
    those.
    \/z+refusing archive member with absolute path .c              3  &   K   | ]	  }|d k(    yw)..N ).0ps     r   	<genexpr>z$_safe_member_name.<locals>.<genexpr>8   s     
$19
$s   z-refusing archive member with '..' component:  )replace
startswithr   is_absoluter   partsanystr)namenormr   r!   s       r   _safe_member_namer&   ,   s     <<c"Ds}T2>>@FthOPP%d+11>1Q#XQ>E>

$e
$$HQRR).3}e$%6B6 ?s   "
B(-B(c                    t               }d}| D ]?  }t        |      j                  }|s|j                  |d          t	        |      dkD  s>d}A t	        |      dk(  r|rt        t        |            S dS )a  Return the single top-level directory shared by every archive entry, or
    ``""`` if the entries don't all live under one common directory.

    Skill bundles are packaged wrapped in one directory named after the skill
    (e.g. ``pdf/SKILL.md``, ``pdf/scripts/...``). The extractor strips that
    wrapper so the contents land directly in the skill's destination directory
    instead of a redundant nested ``<skill>/<skill>/`` level.
    Fr      Tr   )setr   r!   addlennextiter)namestops
has_nestednr!   s        r   _archive_top_dirr2   =   sw     UDJ a &&qu:>J  #4yA~*4T
D"D    c                    |s| S t        |       j                  }|r"|d   |k(  r|dd }|rt        t        |       S dS | S )zDrop the leading ``top`` component from ``safe`` (an already-confined
    relative path). Returns ``""`` for the bare top-dir entry itself.r   r(   Nr   )r   r!   r#   )safetopr!   rests       r   
_strip_topr8   R   sP     $%%EqSQRy,0s=$'(8b8Kr3   c                    | dz  rdS dS )a  Reduce an archive entry's Unix mode to ``0o755`` if it is executable,
    ``0o644`` otherwise.

    Skill bundles can ship executable scripts (e.g. ``scripts/foo.sh``), so the
    execute bit recorded in the archive must survive extraction or invoking the
    script directly fails with permission denied. The mode is deliberately
    collapsed to one of two values: this preserves "is it executable" while
    never propagating setuid/setgid/sticky or group/other-write bits from a
    (possibly third-party) archive.
    I   i  i  r   )src_modes    r   _archive_file_moder<   ^   s     u$5/%/r3   c           	        |j                  ddt               |j                         }t        j                  |       rvt        j
                  |       5 }|j                         }|D cg c]  }t        |j                        x}s| }}t        |      }|D ]  }t        t        |j                        |      }	|	s&||	z  j                         }
t        |
|      st        d|j                        |j                         r|
j                  dd       |
j                  j                  dd       |j                  |      5 }t        |
d      5 }t!        j"                  ||       ddd       ddd       t%        j&                  |
t)        |j*                  dz	                	 ddd       yt-        j                  | d	      5 }|j/                         D cg c]7  }|j1                         r|j3                         r%|j5                         r6|9 }}|D cg c]  }t        |j6                        x}s| }}t        |      }|D ]  }t        t        |j6                        |      }	|	s&||	z  j                         }
t        |
|      st        d
|j6                        |j9                         r|
j                  dd       |
j                  j                  dd       |j;                  |      }||5 }t        |
d      5 }t!        j"                  ||       ddd       ddd       t%        j&                  |
t)        |j<                                	 ddd       yc c}w # 1 sw Y   
xY w# 1 sw Y   xY w# 1 sw Y   yxY wc c}w c c}w # 1 sw Y   |xY w# 1 sw Y   xY w# 1 sw Y   yxY w)a  Extract a skill download (a zip or tar.* archive) from disk into ``dest``.

    Skill bundles are wrapped in a single directory named after the skill; that
    wrapper is stripped so files land directly under ``dest`` rather than a
    redundant ``dest/<skill>/`` level. Skills can be third-party, so this
    refuses any member that would escape ``dest`` (zip-slip / tar-slip) and
    skips symlink/hardlink/device members in tar archives.
    Tparentsexist_okmodez&refusing to extract unsafe zip member )r?   r@   wbN   zr:*)rA   z&refusing to extract unsafe tar member )mkdir_SKILL_DIR_MODEresolvezipfile
is_zipfileZipFileinfolistr&   filenamer2   r8   r   r   is_dirparentopenshutilcopyfileobjoschmodr<   external_attrtarfile
getmembersissymislnkisdevr$   isdirextractfilerA   )archive_pathdestr   zfinfosinfos
safe_namesr6   r5   targetsrcouttfmmembersmember	extracteds                     r   _extract_skill_archiverj   l   sI    	JJtdJA<<>D,'__\* 	ObKKME ).Y7H7W2W!2W!YJY":.C O!"3DMM"BCH+..0vt,$'MdmmM^%_``;;=LLL=##D4#@WWT] 1c4+= 1&&sC01 1 !3D4F4F"4L!MNO	O, 	 
l	/ >2 mmo[aggi1779PQPWPWPY1[[!(MA2CAFF2K-KQ-KaM
Mz* 	>F/<cBDTk**,F64( #I&++!YZZ||~TD9MMt<v.I  -c4#5 -""3,- -HHV/<=!	>	> >+ Z1 1 1 1#	O, 	 \M - - - -%> >s   N)M/M/
B>NNM4	,N47NN<N5NNNN<!N>NCN<N0N$	)N014N</N4M>9NNNN
N<$N-)N00N95N<<Oc                l  K   |j                         r|S d}| j                  j                  j                  j	                  |      2 3 d{   }|j
                  j                         s$|"t        |j
                        t        |      kD  sH|j
                  }U7 P6 |t        d|d|d      |S w)uN  Resolve ``version`` to the concrete numeric timestamp the
    ``/v1/skills/{id}/versions/{version}`` endpoints require.

    ``session.agent.skills[].version`` may be an alias such as ``"latest"``,
    which those endpoints reject — so list the skill's versions and pick the
    newest. Numeric versions are returned unchanged.
    Nzskill z$ has no concrete version to resolve z against)isdigitbetaskillsversionslistversionintr   )clientskill_idrq   newestvs        r   _resolve_skill_versionrw      s      F;;%%..33H=  a99FNc!))ns6{6RYYF= ~6(-QRYQ\\deffMs6   AB4B
BBB4*#B4B4BB4c                 K   | j                   j                  j                  |       d{   }t        t	        j                  |      dz  j                          d{         }g }|j                  j                  D ]  }	 t        | |j                  |j                         d{   }| j                   j                  j                  j                  ||j                         d{   }t        j                  j                  |j                  j!                               xs |j                  }	|	dv r|j                  }	t        t	        j                  |      |	z  j                          d{         }
t#        |
|      s"t$        j'                  d|j                         +t	        j                  |
      }|j)                          d{   r|j+                          d{    t-        t/        t0        j2                  |
d             d{    t5        | |j                  ||
       d{    |j7                  |
       t$        j9                  d|j                  ||
        |S 7 I7 7 7 7 7 7 7 o7 Q# t:        $ r,}t$        j'                  d	|j                  |       Y d}~<d}~ww xY ww)
a'  Download the session agent's skills into ``{workdir}/skills/<name>/``.

    Looks up the session's resolved agent, and for each skill fetches its files
    via ``client.beta.skills.versions.download`` and extracts the archive under a
    directory named after the skill. The archive is streamed to a temp file
    rather than buffered whole in memory. A failure on one skill is logged and
    does not block the others.

    Returns the list of skill directories that were created, so the caller can
    remove them when the workdir is torn down.
    Nrn   rt   )r   r   r   z.skill name %r escapes the skills dir; skippingT)ignore_errorsz-downloaded skill skill_id=%s version=%s -> %sz(failed to download skill skill_id=%s: %s)rm   sessionsretriever   anyiorF   agentrn   rw   rt   rq   ro   rQ   pathbasenamer$   stripr   logwarning
is_symlinkunlinkr   r   rO   rmtree_download_and_extractappendr_   	Exception)rs   
session_idworkdirsessionskills_root
downloadedskill
version_idrq   dirnamer\   adestes                r   r   r      s$      KK((11*==Gejj1H<EEGGHK  J%% W	W5fennemm\\J"KK..77@@V[VdVd@eeG gg&&w||'9'9';<NG/)..uzz+6@IIKKLD4-Lgll[JJt$E%%'''lln$$76==$dKLLL'
DQQQd#HHDennV`bfg)W. 9 >G ]e L
 ($LQ  	WKKBENNTUVV	Ws   )KI42KI7$K$J(I:)AJ+I=,BJ3J 
44J(K*(JJJ*J+,JJJ7J	87J/K7K:J=J JJJJ	J	K !J;5K;K  Kc                ,  K   t        j                  |j                        j                  ddt               d{    t        t        t        j                  dd|j                               d{   \  }}t        j                  |       t        j                  |      }	 | j                  j                  j                  j                  j                  ||      4 d{   }|j!                  |       d{    ddd      d{    t        t"        t        |      |       d{    |j%                  d       d{    y7 7 7 o7 X7 J# 1 d{  7  sw Y   ZxY w7 ?7 '# |j%                  d       d{  7   w xY ww)	zGStream the skill archive to a temp file, then extract it into ``dest``.Tr>   Nz.skill-z.archive)prefixsuffixdirry   )
missing_ok)r}   r   rM   rD   rE   r   r   tempfilemkstemprQ   closerm   rn   ro   with_streaming_responsedownloadstream_to_filerj   r   )rs   rt   r   r\   fdtmp_nametmparchives           r   r   r      sH    
**T[[
!
'
't/
'
ZZZ!'(*:*:9U_eiepep"qrrLBHHRL
**X
C*;;%%..FFOO P 
 	3 	3((222	3 	3
 -tH~tDDDjjDj))) [r	3 3	3 	3 	3 	3
 	E)cjjDj)))s   9FE7F3E41F&?E4 %E&E4 )E>E?EE4 E!E4 0E01E4 5F
E2FFE4 EE4 E-!E$"E-)E4 2F4F
FFF)r   r   r   r   returnbool)r$   r#   r   r#   )r.   z	list[str]r   r#   )r5   r#   r6   r#   r   r#   )r;   rr   r   rr   )r[   r   r\   r   r   None)rs   r
   rt   r#   rq   r#   r   r#   )rs   r
   r   r#   r   zstr | os.PathLike[str]r   z
list[Path])
rs   r
   rt   r#   r   r#   r\   r   r   r   )"__doc__
__future__r   rQ   rO   loggingrT   rG   r   typingr   pathlibr   r   	functoolsr   r}   anyio.to_threadr   _clientr
   __all__rE   	getLoggerr   r   r&   r2   r8   r<   rj   rw   r   r   r   r3   r   <module>r      s    # 	        '   $)$
% g;<7"E*	0:>z&,,+.,9O,,^*r3   