§
    ÷žyjî  ã                  óÀ   — U d Z ddlmZ ddlZddlZddlmZmZmZ ddl	m
Z
  ej        e¦  «        Zi Zded<    ej        ¦   «         Zdd„Zdd„Zdd„ZdZdd„Zdd„ZdS )uÍ  
Browser Provider Registry
=========================

Central map of registered cloud browser providers. Populated by plugins at
import-time via :meth:`PluginContext.register_browser_provider`; consumed by
:func:`tools.browser_tool._get_cloud_provider` to route each cloud-mode
``browser_*`` tool call to the active backend.

Active selection
----------------
The active provider is chosen by configuration with this precedence:

1. ``browser.cloud_provider`` in ``config.yaml`` (explicit override).
2. Legacy preference order â€” ``browser-use`` â†’ ``browserbase`` â€” filtered by
   availability. Matches the historic auto-detect order in
   :func:`tools.browser_tool._get_cloud_provider` (Browser Use checked first
   because it covers both the managed Nous gateway and direct API key path;
   Browserbase as the older direct-credentials fallback). ``firecrawl`` is
   intentionally NOT in the legacy walk â€” users only get Firecrawl as a
   cloud browser when they explicitly set ``browser.cloud_provider:
   firecrawl``, matching pre-migration behaviour where Firecrawl was never
   auto-selected.
3. Otherwise ``None`` â€” the dispatcher falls back to local browser mode.

The explicit-config branch (rule 1) intentionally ignores ``is_available()``
so the dispatcher surfaces a typed "X_API_KEY is not set" error to the user
instead of silently switching backends. Matches the legacy
:func:`tools.browser_tool._get_cloud_provider` behaviour for configured names.

Note: there is no "capability" split here (unlike the web subsystem, which
has search/extract/crawl). Every browser provider implements the full
:class:`agent.browser_provider.BrowserProvider` lifecycle; the registry's
job is purely selection, not capability routing.
é    )ÚannotationsN)ÚDictÚListÚOptional)ÚBrowserProviderzDict[str, BrowserProvider]Ú
_providersÚproviderr   ÚreturnÚNonec                ó<  — t          | t          ¦  «        s$t          dt          | ¦  «        j        › �¦  «        ‚| j        }t          |t          ¦  «        r|                     ¦   «         st          d¦  «        ‚t          5  t                               |¦  «        }| t          |<   ddd¦  «         n# 1 swxY w Y   |�0t                               d|t          |¦  «        j        ¦  «         dS t                               d|t          | ¦  «        j        ¦  «         dS )uÑ   Register a cloud browser provider.

    Re-registration (same ``name``) overwrites the previous entry and logs
    a debug message â€” makes hot-reload scenarios (tests, dev loops) behave
    predictably.
    z<register_provider() expects a BrowserProvider instance, got z1Browser provider .name must be a non-empty stringNz,Browser provider '%s' re-registered (was %r)z%Registered browser provider '%s' (%s))Ú
isinstancer   Ú	TypeErrorÚtypeÚ__name__ÚnameÚstrÚstripÚ
ValueErrorÚ_lockr   ÚgetÚloggerÚdebug)r	   r   Úexistings      ú</home/ragecks/.hermes/hermes-agent/agent/browser_registry.pyÚregister_providerr   4   s`  € õ �h¥Ñ0Ô0ð 
Ýð-Ý˜‘>”>Ô*ð-ð -ñ
ô 
ð 	
ð Œ=€DÝ�d�CÑ Ô ð N¨¯
ª
©¬ð NÝÐLÑMÔMÐMÝ	ð $ð $Ý—>’> $Ñ'Ô'ˆØ#�
�4Ñð$ð $ð $ñ $ô $ð $ð $ð $ð $ð $ð $øøøð $ð $ð $ð $ð ÐÝ�ŠØ:Ø•$�x‘.”.Ô)ñ	
ô 	
ð 	
ð 	
ð 	
õ
 	�ŠØ3Ø•$�x‘.”.Ô)ñ	
ô 	
ð 	
ð 	
ð 	
s   Â %B1Â1B5Â8B5úList[BrowserProvider]c                 ó°   — t           5  t          t                               ¦   «         ¦  «        } ddd¦  «         n# 1 swxY w Y   t	          | d„ ¬¦  «        S )z0Return all registered providers, sorted by name.Nc                ó   — | j         S )N©r   )Úps    r   ú<lambda>z list_providers.<locals>.<lambda>V   s   €  q¤v€ ó    )Úkey)r   Úlistr   ÚvaluesÚsorted)Úitemss    r   Úlist_providersr(   R   s�   € å	ð *ð *Ý•Z×&Ò&Ñ(Ô(Ñ)Ô)ˆð*ð *ð *ñ *ô *ð *ð *ð *ð *ð *ð *øøøð *ð *ð *ð *å�%Ð-Ð-Ð.Ñ.Ô.Ð.s   ˆ';»?Á?r   r   úOptional[BrowserProvider]c                óÊ   — t          | t          ¦  «        sdS t          5  t                               |                      ¦   «         ¦  «        cddd¦  «         S # 1 swxY w Y   dS )z5Return the provider registered under *name*, or None.N)r   r   r   r   r   r   r   s    r   Úget_providerr+   Y   sš   € å�d�CÑ Ô ð ØˆtÝ	ð ,ð ,Ý�~Š~˜dŸjšj™lœlÑ+Ô+ð,ð ,ð ,ð ,ñ ,ô ,ð ,ð ,ð ,ð ,ð ,ð ,øøøð ,ð ,ð ,ð ,ð ,ð ,s   Ÿ,AÁAÁA)zbrowser-useÚbrowserbaseÚ
configuredúOptional[str]c                óP  — t           5  t          t          ¦  «        }ddd¦  «         n# 1 swxY w Y   d	d„}| dk    rdS | r4|                     | ¦  «        }|�|S t                               d| ¦  «         t          D ](}|                     |¦  «        }|� ||¦  «        r|c S Œ)dS )
uÒ  Resolve the active browser provider.

    Resolution rules (in order):

    1. **Explicit "local".** Returns None â€” the dispatcher disables cloud
       mode entirely. Mirrors legacy short-circuit in
       :func:`tools.browser_tool._get_cloud_provider`.
    2. **Explicit config wins, ignoring availability.** If ``configured``
       names a registered provider, return it even if its
       :meth:`is_available` returns False â€” the dispatcher will surface a
       precise "X_API_KEY is not set" error instead of silently routing
       somewhere else.
    3. **Legacy preference walk, filtered by availability.** Walk
       :data:`_LEGACY_PREFERENCE` (``browser-use`` â†’ ``browserbase``) looking
       for a provider whose ``is_available()`` is True.

    There is intentionally NO "single-eligible shortcut" rule here (unlike
    :func:`agent.web_search_registry._resolve`). Pre-migration, the
    auto-detect branch in ``tools.browser_tool._get_cloud_provider`` only
    considered Browser Use and Browserbase; Firecrawl was reachable only
    via an explicit ``browser.cloud_provider: firecrawl`` config key.
    Preserving that gate matters because Firecrawl shares its API key with
    the *web* extract plugin (``plugins/web/firecrawl/``), so users who set
    ``FIRECRAWL_API_KEY`` for web extract must NOT get silently routed to a
    paid cloud browser on a fresh install. Third-party browser-provider
    plugins added under ``~/.hermes/plugins/browser/<vendor>/`` are subject
    to the same gate â€” they must be explicitly configured to take effect.

    Returns None when no provider is configured AND no available provider
    matches the legacy preference; the dispatcher then falls back to local
    browser mode.
    Nr    r   r
   Úboolc                ó¼   — 	 t          |                      ¦   «         ¦  «        S # t          $ r.}t                               d| j        |d¬¦  «         Y d}~dS d}~ww xY w)zDWrap ``is_available()`` so a buggy provider doesn't kill resolution.uH   Browser provider %s.is_available() raised %s â€” treating as unavailableT)Úexc_infoNF)r0   Úis_availableÚ	Exceptionr   Úwarningr   )r    Úexcs     r   Ú_is_available_safez$_resolve.<locals>._is_available_safe•   st   € ð	Ý˜ŸšÑ(Ô(Ñ)Ô)Ð)øÝð 	ð 	ð 	Ý�NŠNØZØ”˜ dð ñ ô ð ð �5�5�5�5�5øøøøð	øøøs   ‚ # £
A­#AÁAÚlocalzVbrowser cloud_provider '%s' configured but not registered; falling back to auto-detect)r    r   r
   r0   )r   Údictr   r   r   r   Ú_LEGACY_PREFERENCE)r-   Úsnapshotr7   r	   Úlegacys        r   Ú_resolver=   q   s  € õB 
ð $ð $Ý�
Ñ#Ô#ˆð$ð $ð $ñ $ô $ð $ð $ð $ð $ð $ð $øøøð $ð $ð $ð $ð	ð 	ð 	ð 	ð �WÒÐØˆtð
 ð 
Ø—<’< 
Ñ+Ô+ˆØÐØˆOÝ�Šð*àñ	
ô 	
ð 	
õ %ð ð ˆØ—<’< Ñ'Ô'ˆØÐÐ$6Ð$6°xÑ$@Ô$@ÐØˆOˆOˆOøàˆ4s   ˆ)©-°-c                 óx   — t           5  t                               ¦   «          ddd¦  «         dS # 1 swxY w Y   dS )z"Clear the registry. **Test-only.**N)r   r   Úclear© r"   r   Ú_reset_for_testsrA   ½   s~   € å	ð ð Ý×ÒÑÔÐðð ð ñ ô ð ð ð ð ð ð ð øøøð ð ð ð ð ð s   ˆ/¯3¶3)r	   r   r
   r   )r
   r   )r   r   r
   r)   )r-   r.   r
   r)   )r
   r   )Ú__doc__Ú
__future__r   ÚloggingÚ	threadingÚtypingr   r   r   Úagent.browser_providerr   Ú	getLoggerr   r   r   Ú__annotations__ÚLockr   r   r(   r+   r:   r=   rA   r@   r"   r   ú<module>rK      s  ðð"ð "ð "ðH #Ð "Ð "Ð "Ð "Ð "à €€€Ø Ð Ð Ð Ø 'Ð 'Ð 'Ð 'Ð 'Ð 'Ð 'Ð 'Ð 'Ð 'à 2Ð 2Ð 2Ð 2Ð 2Ð 2à	ˆÔ	˜8Ñ	$Ô	$€ð *,€
Ð +Ð +Ð +Ñ +Øˆ	ŒÑÔ€ð
ð 
ð 
ð 
ð</ð /ð /ð /ð,ð ,ð ,ð ,ð$Ð ðIð Ið Ið IðXð ð ð ð ð r"   