
    (Gj9                       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ZddlZddl	m
Z
 ddlmZmZmZ  ej        d          Zddd	d
ddddgddddd
dddd
dddd
dddd
dddd
dddd
dddd
dddd
dddd
dddd
ddd d
ddd!d
d"Zd#ed$<   i Zd%ed&<   i Zd'ed(<    ej                    Zd)ZdEd,ZdFd.ZdGd1ZdHd5ZdId8ZdJdKd;ZdLd<Z	 dMdNd@ZdOdAZ dOdBZ!dPdCZ"g dDZ#dS )Qu  Auto-installation of LSP server binaries.

Tries to install missing servers using whatever package manager is
appropriate.  All installs go to a Hermes-owned bin staging dir,
``<HERMES_HOME>/lsp/bin/``, so we don't pollute the user's global
toolchain.

Strategies:

- ``auto`` — attempt to install with the best available package
  manager.  This is the default.
- ``manual`` — never install; if a binary is missing, the server is
  silently skipped and the user is told about it via ``hermes lsp
  status``.
- ``off`` — same as ``manual`` for now (kept distinct so we can
  evolve behavior later, e.g. logging differently).

The actual installs happen synchronously the first time a server is
needed and concurrent calls to :func:`try_install` for the same
package are deduplicated via a per-package lock.

Failure modes are non-fatal: every install path is wrapped in
try/except and returns ``None`` on failure.  The tool layer then
falls back to its in-process syntax checker, exactly as if the user
hadn't enabled LSP at all.
    )annotationsN)Path)AnyDictOptionalzagent.lsp.installnpmpyrightzpyright-langserver)strategypkgbintypescript-language-server
typescript)r
   r   r   
extra_pkgs@vue/language-serverzvue-language-serversvelte-language-serversvelteserver@astrojs/language-serverzastro-lsyaml-language-serverbash-language-serverintelephense!dockerfile-language-server-nodejszdocker-langservergozgolang.org/x/tools/gopls@latestgoplsmanual rust-analyzerclangdlua-language-serverpwsh)r	   r   r   r   r   r   r   r   r   r   r   r   r   
powershellzDict[str, Dict[str, Any]]INSTALL_RECIPESzDict[str, threading.Lock]_install_lockszDict[str, Optional[str]]_install_results)z.cmd.exez.batreturnboolc                 "    t           j        dk    S )Nnt)osname     7/home/rurouni/.hermes/hermes-agent/agent/lsp/install.py_is_windowsr.   w   s    7d?r,   r   c                    t           j                            d          } | =t           j                            t           j                            d          d          } t          |           dz  dz  }|                    dd           |S )	z8Return the Hermes-owned bin staging dir for LSP servers.HERMES_HOMEN~z.hermeslspr   Tparentsexist_ok)r)   environgetpathjoin
expanduserr   mkdir)homeps     r-   hermes_lsp_bin_dirr>   {   so    :>>-((D|w||BG..s33Y??T

UU"AGGD4G(((Hr,   base
list[Path]c                \   | g}t                      rt          |                                           h}t          D ]p}t	          t          |           |z             }t          |                                          }||vr*|                    |           |                    |           q|S )zAReturn platform-native executable candidates for a staged binary.)r.   strlower_WINDOWS_WRAPPER_SUFFIXESr   appendadd)r?   
candidatesexistingsuffix	candidatekeys         r-   _native_binary_candidatesrL      s    J}} "IIOO%%&/ 	" 	"FSYY/00Ii..&&((C(""!!),,,S!!!r,   r*   rB   Optional[str]c                l   t          t                      | z            D ]F}|                                r0t          j        |t          j                  rt          |          c S Gt          j        |           }|r|S t                      r(t          D ] }t          j        |  |           }|r|c S !dS )z9Probe the staging dir + PATH for a binary named ``name``.N)rL   r>   existsr)   accessX_OKrB   shutilwhichr.   rD   )r*   stagedon_pathrI   s       r-   _existing_binaryrV      s    +,>,@,@4,GHH  ==?? 	ry99 	v;;l4  G }} / 	 	Fld#4F#4#455G 4r,   r   threading.Lockc                    t           5  t                              |           }|t          j                    }|t          | <   |cd d d            S # 1 swxY w Y   d S N)_install_lock_metar"   r7   	threadingLock)r   locks     r-   	_get_lockr^      s    	  !!#&&<>##D"&N3                 s   ;AAAautor
   c                   |dvr@t                               | i           }|                    d|           }t          |          S | t          v rt          |          S t	          |           }|5  | t          v rt          |          cddd           S t          |           }|t          | <   |cddd           S # 1 swxY w Y   dS )u  Try to install ``pkg`` and return the binary path if successful.

    ``strategy`` is ``"auto"``, ``"manual"``, or ``"off"``.  In
    ``manual``/``off`` mode, this function only probes for an
    existing binary and returns ``None`` if not found.

    The install is cached per-package — a second call returns the
    same path (or ``None``) without reinstalling.  Concurrent calls
    are serialized.
    >   r_   r   N)r!   r7   rV   r#   r^   _do_install)r   r
   recipebin_namer]   results         r-   try_installre      s,    y   !$$S"--::eS)))))
$$S>>D	  """#C(        S!! &                 s   ,B6B66B:=B:c                   t                               |           }|t          j        |           S |                    dd          }|                    d|           }t	          |          }|r|S |dk    rt
                              d| |           d S |dk    r;t          |                    d|           ||                    d          pg           S |d	k    r$t          |                    d|           |          S |d
k    r$t          |                    d|           |          S t
          
                    d||            d S )Nr
   r   r   z0[install] %s requires manual install (recipe=%s)r   r   r   )r   r   pipz$[install] unknown strategy %r for %s)r!   r7   rR   rS   rV   loggerdebug_install_npm_install_go_install_pipwarning)r   rb   r
   rc   rH   s        r-   ra   ra      sM     %%F~|C   zz*h//Hzz%%%H  ))H 8GfUUUt5JJuc""zz,//52
 
 
 	

 46::eS118<<<5FJJuc22H===
NN98SIII4r,   rc   r   Optional[list]c           	        t          j        d          }|t                              d|            dS t	                      j        }| gt          |pg           z   }	 t                              d|d                    |                     t          j	        |ddt          |          dd	d
g|ddddt          j                  }|j        dk    r=t                              d| |j                                        dd                    dS n@# t          j        t"          f$ r'}t                              d| |           Y d}~dS d}~ww xY w|dz  dz  |z  }t%          |          D ]}	|	                                rt	                      |	j        z  }
|
                                sh	 |
                    |	           nQ# t"          t,          f$ r= 	 t          j        |	|
           n## t"          $ r t          |	          cY cY c S w xY wY nw xY wt          |
                                r|
n|	          c S t                              d| |           dS )a  Install an npm package into our staging dir.

    Uses ``npm install --prefix`` so the binaries land in
    ``<staging>/node_modules/.bin/<bin_name>`` and we symlink them up
    one level for direct PATH-style access.

    ``extra_pkgs`` is a list of sibling packages to install in the
    same ``node_modules`` tree.  Used for LSP servers with runtime
    peer deps that npm doesn't auto-pull (typescript-language-server
    needs ``typescript`` next to it; intelephense ships standalone).
    r   Nz,[install] cannot install %s: npm not on PATHz$[install] npm install --prefix %s %s installz--prefixz--silentz	--no-fundz
--no-auditFT,  checkcapture_outputtexttimeoutstdinr   z'[install] npm install failed for %s: %s  z([install] npm install errored for %s: %snode_modulesz.binz;[install] npm install for %s succeeded but bin %s not found)rR   rS   rh   infor>   parentlistr9   
subprocessrunrB   DEVNULL
returncoderm   stderrstripTimeoutExpiredOSErrorrL   rO   r*   
symlink_toNotImplementedErrorcopy2)r   rc   r   r   staginginstall_targetsprocenm_binclinks              r-   rj   rj      s     ,u

C
{BCHHHt "")Ged:#3444O2HH_%%	
 	
 	

 ~)ZWz;P\o_no$
 
 
 ?aNN93@Q@Q@S@STXUXTX@Y   4	  
 %w/   A3JJJttttt
 ~%.9F&v.. 5 588:: 	5%''!&0D;;== &&OOA&&&&!45 & & &&Q----" & & &"1vv& .-& t{{}}3tt!44444	5 NNPRUW_```4sO   B/D E%EE*G  HG('H(HHHHHc           	     H   t          j        d          }|t                              d|            dS t	                      }t          t          j                  }t          |          |d<   	 t                              d| |           t          j
        |d| gdddd	|t          j        
          }|j        dk    r=t                              d| |j                                        dd                    dS n@# t          j        t"          f$ r'}t                              d| |           Y d}~dS d}~ww xY w||z  }t%                      r|                    d          }|                                rt          |          S t                              d| |           dS )z'Install a Go module to GOBIN=<staging>.r   Nz+[install] cannot install %s: go not on PATHGOBINz"[install] go install %s (GOBIN=%s)rq   FTiX  )rt   ru   rv   rw   envrx   r   z&[install] go install failed for %s: %sry   z'[install] go install errored for %s: %sr$   z:[install] go install for %s succeeded but bin %s not found)rR   rS   rh   r{   r>   dictr)   r6   rB   r~   r   r   r   rm   r   r   r   r   r.   with_suffixrO   )r   rc   r   r   r   r   r   bin_paths           r-   rk   rk   -  s   	d		B	zA3GGGt ""G
rz

Cw<<CL8#wGGG~C $
 
 
 ?aNN8#t{?P?P?R?RSWTWSW?X   4	  
 %w/   @#qIIIttttt !H}} 0''// 8}}
NNOQTV^___4s   .B
C; ;D8D33D8c           
     `   t                      j        dz  }|                    dd           	 t                              d||            t          j        t          j        ddddt          |          d	| gd
dddt
          j
                  }|j        dk    r=t                              d| |j                                        dd                    dS n@# t
          j        t           f$ r'}t                              d| |           Y d}~dS d}~ww xY w|dz  g}t#                      r|                    |dz             |D ]}t'          ||z            D ]}|                                rt                      |j        z  }|                                sj	 |                    |           nS# t           t.          f$ r? 	 t1          j        ||           n%# t           $ r t          |          cY cY c c S w xY wY nw xY wt          |                                r|n|          c c S ԌdS )a;  Install a Python package into a hermes-owned target dir.

    We avoid polluting the user's site-packages by using
    ``pip install --target``.  Bins go into
    ``<staging>/python-packages/bin/`` which we symlink into
    ``<staging>/bin``.  Note: this only works for packages that ship a
    console script.
    zpython-packagesTr3   z$[install] pip install --target %s %sz-mrg   rq   z--targetz--quietFrr   rs   r   z'[install] pip install failed for %s: %sNry   z([install] pip install errored for %s: %sr   Scripts)r>   r|   r;   rh   r{   r~   r   sys
executablerB   r   r   rm   r   r   r   r   r.   rE   rL   rO   r*   r   r   rR   r   )	r   rc   
pip_targetr   r   script_dirs
script_dirr   r   s	            r-   rl   rl   R  s    $%%,/@@JTD111:JLLL~^T5)ZZR[]`a$
 
 
 ?aNN93@Q@Q@S@STXUXTX@Y   4	  
 %w/   A3JJJttttt
 %&K}} 3:	1222! @ @
1*x2GHH 	@ 	@H   
@)++hm;{{}} 111111#%89 1 1 11"L48888& 1 1 1#&x==0000000001 981
 4;;==>44h???????
@	@ 4sN   B%C D-DDF00H GH G:	1H 9G:	:H ?H c                    t                               |           }|r|                    d|           n| }t          |          rdS |r|                    d          dk    rdS dS )zReturn ``installed``, ``missing``, or ``manual-only`` for a package.

    Used by the ``hermes lsp status`` CLI to give users a quick
    overview of what's available without spawning anything.
    r   	installedr
   r   zmanual-onlymissing)r!   r7   rV   )r   rb   rc   s      r-   detect_statusr     sq       %%F)/8vzz%%%%SH!! { &**Z((H44}9r,   )r!   re   r   r>   )r%   r&   )r%   r   )r?   r   r%   r@   )r*   rB   r%   rM   )r   rB   r%   rW   )r_   )r   rB   r
   rB   r%   rM   )r   rB   r%   rM   rY   )r   rB   rc   rB   r   rn   r%   rM   )r   rB   rc   rB   r%   rM   )r   rB   r%   rB   )$__doc__
__future__r   loggingr)   rR   r~   r   r[   pathlibr   typingr   r   r   	getLoggerrh   r!   __annotations__r"   r#   r\   rZ   rD   r.   r>   rL   rV   r^   re   ra   rj   rk   rl   r   __all__r+   r,   r-   <module>r      s    4 # " " " " "  				      



           & & & & & & & & & &		.	/	/ ")<PQQ ++
 $n	# 	# %$  '  )! ! %%  %% 
 "'~nUU2"* * 'HQXYY #+2oNN#Bx@@ )1DYZZ
  (6BBw<. <. < < < <~ -/ . . . .-/  / / / /#Y^%% 4                     >   H "&> > > > >B" " " "J/ / / /d     r,   