
    j3                       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!Zd"ed#<   i Zd$ed%<   i Zd&ed'<    ej                    Zd>d*Zd?d.Zd@d1ZdAdBd4ZdCd5Z	 dDdEd9ZdFd:ZdFd;ZdGd<Zg d=Z dS )Hu  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)r	   r   r   r   r   r   r   r   r   r   r   r   r   zDict[str, Dict[str, Any]]INSTALL_RECIPESzDict[str, threading.Lock]_install_lockszDict[str, Optional[str]]_install_resultsreturnr   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)osenvirongetpathjoin
expanduserr   mkdir)homeps     0/usr/local/lib/hermes-agent/agent/lsp/install.pyhermes_lsp_bin_dirr4   q   so    :>>-((D|w||BG..s33Y??T

UU"AGGD4G(((H    namestrOptional[str]c                    t                      | z  }|                                r.t          j        |t          j                  rt          |          S t          j        |           }|r|S dS )z9Probe the staging dir + PATH for a binary named ``name``.N)r4   existsr*   accessX_OKr7   shutilwhich)r6   stagedon_paths      r3   _existing_binaryrA   {   sd    !!D(F}} 29VRW55 6{{l4  G 4r5   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    r,   	threadingLock)r   locks     r3   	_get_lockrI      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.
    >   rJ   r   N)r   r,   rA   r!   rI   _do_install)r   r
   recipebin_namerH   results         r3   try_installrP      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   r,   r=   r>   rA   loggerdebug_install_npm_install_go_install_pipwarning)r   rM   r
   rN   existings        r3   rL   rL      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4r5   rN   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          }|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"          j        dk    r||                    d          g}	n|g}	|	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timeoutr   z'[install] npm install failed for %s: %s  z([install] npm install errored for %s: %snode_modulesz.binntz.cmdz;[install] npm install for %s succeeded but bin %s not found)r=   r>   rS   infor4   parentlistr.   
subprocessrunr7   
returncoderX   stderrstripTimeoutExpiredOSErrorr*   r6   with_suffixr:   
symlink_toNotImplementedErrorcopy2)r   rN   r   r   staginginstall_targetsprocenm_bin
candidatesclinks               r3   rU   rU      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	w$f00889

X
 5 588:: 	5%''!&0D;;== &&OOA&&&&!45 & & &&Q----" & & &"1vv& .-& t{{}}3tt!44444	5 NNPRUW_```4sO   B$D ED<<E=GH!%G;:H!;HH!HH! H!c                6   t          j        d          }|t                              d|            dS t	                      }t          t          j                  }t          |          |d<   	 t                              d| |           t          j
        |d| gdddd	|
          }|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          j        dk    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)r]   FTiX  )r`   ra   rb   rc   envr   z&[install] go install failed for %s: %srd   z'[install] go install errored for %s: %srf   z.exez:[install] go install for %s succeeded but bin %s not found)r=   r>   rS   rg   r4   dictr*   r+   r7   rj   rk   rl   rX   rm   rn   ro   rp   r6   rq   r:   )r   rN   r   ru   r   rw   rx   bin_paths           r3   rV   rV     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	w$''// 8}}
NNOQTV^___4s   .A?C0 0D-D((D-c           
        t                      j        dz  }|                    dd           	 t                              d||            t          j        t          j        ddddt          |          d	| gd
ddd          }|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  |z  }|                                rt                      |z  }|                                sf	 |                    |           nO# t          t$          f$ r; 	 t'          j        ||           n!# t          $ r t          |          cY cY S w xY wY nw xY wt          |                                r|n|          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-packagesTr'   z$[install] pip install --target %s %sz-mrR   r]   z--targetz--quietFr^   r_   r   z'[install] pip install failed for %s: %sNrd   z([install] pip install errored for %s: %sr   )r4   rh   r0   rS   rg   rj   rk   sys
executabler7   rl   rX   rm   rn   ro   rp   r:   rr   rs   r=   rt   )r   rN   
pip_targetrw   rx   r   r|   s          r3   rW   rW   8  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 E!H,H 
8!##h.{{}} 	))))))01 ) ) ))L40000 ) ) )x==((((() 10)
 4;;==644h7774sN   BC D	"DD	E$ $F06FF0F*%F0)F**F0/F0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   r,   rA   )r   rM   rN   s      r3   detect_statusr   d  sq       %%F)/8vzz%%%%SH!! { &**Z((H44}9r5   )r   rP   r   r4   )r"   r   )r6   r7   r"   r8   )r   r7   r"   rB   )rJ   )r   r7   r
   r7   r"   r8   )r   r7   r"   r8   rD   )r   r7   rN   r7   r   rZ   r"   r8   )r   r7   rN   r7   r"   r8   )r   r7   r"   r7   )!__doc__
__future__r   loggingr*   r=   rj   r   rF   pathlibr   typingr   r   r   	getLoggerrS   r   __annotations__r    r!   rG   rE   r4   rA   rI   rP   rL   rU   rV   rW   r   __all__ r5   r3   <module>r      s    4 # " " " " "  				      



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