
    `gj                        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y)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]
_providersc                   t        | t              s!t        dt        |       j                         | j
                  }t        |t              r|j                         st        d      t        5  t        j                  |      }| t        |<   ddd       +t        j                  d|t        |      j                         yt        j                  d|t        |       j                         y# 1 sw Y   axY w)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)providerr   existings      I/root/.hermes/venv/lib/python3.12/site-packages/agent/browser_registry.pyregister_providerr   4   s     h0>**+-
 	
 ==DdC 

LMM	 $>>$'#
4$ :$x.))	

 	3$x.))	
$ $s   /C..C7c                     t         5  t        t        j                               } ddd       t	         d       S # 1 sw Y   xY w)z0Return all registered providers, sorted by name.Nc                    | j                   S )Nr   )ps    r   <lambda>z list_providers.<locals>.<lambda>V   s
    qvv     )key)r   listr   valuessorted)itemss    r   list_providersr%   R   s;    	 *Z&&()*%-..* *s	   ;Ac                    t        | t              syt        5  t        j	                  | j                               cddd       S # 1 sw Y   yxY w)z5Return the provider registered under *name*, or None.N)r
   r   r   r   r   r   r   s    r   get_providerr'   Y   s9    dC 	 ,~~djjl+, , ,s   #AA)zbrowser-usebrowserbasec                   t         5  t        t              }ddd       dd}| dk(  ry| r+j                  |       }||S t        j                  d|        t        D ]"  }j                  |      }| ||      s |c S  y# 1 sw Y   lxY w)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.
    Nc                    	 t        | j                               S # t        $ r-}t        j	                  d| j
                  |d       Y d}~y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)boolis_available	Exceptionr   warningr   )r   excs     r   _is_available_safez$_resolve.<locals>._is_available_safe   sM    	()) 	NNZd   	s    	A#AAlocalzVbrowser cloud_provider '%s' configured but not registered; falling back to auto-detect)r   r   returnr,   )r   dictr   r   r   r   _LEGACY_PREFERENCE)
configuredsnapshotr1   r   legacys        r   _resolver9   q   s    B 
 $
#$	 W
 <<
+O*	
 % <<'$6x$@O
 Q$ $s   BBc                 b    t         5  t        j                          ddd       y# 1 sw Y   yxY w)z"Clear the registry. **Test-only.**N)r   r   clear r   r   _reset_for_testsr=      s&    	   s   %.)r   r   r3   None)r3   zList[BrowserProvider])r   r   r3   Optional[BrowserProvider])r6   zOptional[str]r3   r?   )r3   r>   )__doc__
__future__r   logging	threadingtypingr   r   r   agent.browser_providerr   	getLoggerr   r   r   __annotations__Lockr   r   r%   r'   r5   r9   r=   r<   r   r   <module>rI      so   "H #   ' ' 2			8	$ *,
& +	
</,$ IXr   