
    `gjU4                        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m	Z	 ddl
mZmZmZ  ej                  e      Z e	e      j$                  Z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 G d d      Zy)u  Cron scheduler provider plugin discovery.

Scans two directories for cron scheduler provider plugins:

1. Bundled providers: ``plugins/cron_providers/<name>/`` (shipped with hermes-agent)
2. User-installed providers: ``$HERMES_HOME/plugins/<name>/``

Each subdirectory must contain ``__init__.py`` with a class implementing the
``CronScheduler`` ABC (``cron/scheduler_provider.py``). On name collisions,
bundled providers take precedence.

This is a near-verbatim clone of ``plugins/memory/__init__.py`` — the same
discovery/loader machinery, retargeted at ``CronScheduler``. The built-in
``InProcessCronScheduler`` is NOT discovered here: it is core (lives in
``cron/scheduler_provider.py``) so the fallback can never be accidentally
removed. Only NON-default providers (e.g. "chronos") live under this directory.

Only ONE provider can be active at a time, selected via ``cron.provider`` in
config.yaml (empty = built-in). See ``cron.scheduler_provider.resolve_cron_scheduler``.

Usage:
    from plugins.cron_providers import discover_cron_schedulers, load_cron_scheduler

    available = discover_cron_schedulers()   # [(name, desc, available), ...]
    provider = load_cron_scheduler("chronos")  # CronScheduler instance
    )annotationsN)Path)ListOptionalTuple_hermes_user_cronc                    | t         j                  v ryt        j                  j	                  | dd      }||_        t        j                  j                  |      t         j                  | <   y)u  Register an empty package shell in sys.modules.

    User-installed providers import as ``_hermes_user_cron.<name>``, a dotted
    name whose parents exist nowhere on disk. Unless those parents are present
    in ``sys.modules``, any relative import inside the plugin
    (``from . import config``) fails with
    ``ModuleNotFoundError: No module named '_hermes_user_cron'`` — the same
    reason the loader already registers ``plugins`` and ``plugins.cron_providers`` for
    bundled providers.
    NT)
is_package)sysmodules	importlib	machinery
ModuleSpecsubmodule_search_locationsutilmodule_from_spec)namesearch_locationsspecs      R/root/.hermes/venv/lib/python3.12/site-packages/plugins/cron_providers/__init__.py_register_synthetic_packager   /   sU     s{{))$)FD&6D#!77=CKK    c                 j    	 ddl m}   |        dz  }|j                         r|S dS # t        $ r Y yw xY w)z8Return ``$HERMES_HOME/plugins/`` or None if unavailable.r   )get_hermes_homepluginsN)hermes_constantsr   is_dir	Exception)r   ds     r   _get_user_plugins_dirr    E   s<    4	)HHJq(D( s   !& & 	22c                    | dz  }|j                         sy	 |j                  d      dd }d|v xs d|v S # t        $ r Y yw xY w)	u   Heuristic: does *path* look like a cron scheduler provider plugin?

    Checks for ``register_cron_scheduler`` or ``CronScheduler`` in the
    ``__init__.py`` source. Cheap text scan — no import needed.
    __init__.pyFreplace)errorsNi    register_cron_schedulerCronScheduler)exists	read_textr   )path	init_filesources      r   _is_cron_provider_dirr,   O   sc     }$I$$I$6u=(F2Oo6OO s   7 	AAc                    t               } g }t        j                         rt        t        j	                               D ]z  }|j                         r|j
                  j                  d      r/|dz  j                         sC| j                  |j
                         |j                  |j
                  |f       | t               }|rt        |j	                               D ]f  }|j                         r|j
                  j                  d      r/|j
                  | v r>t        |      sJ|j                  |j
                  |f       h |S )zYield ``(name, path)`` for all discovered provider directories.

    Scans bundled first, then user-installed. Bundled takes precedence on
    name collisions (first-seen wins via ``seen`` set).
    )_.r"   )set_CRON_PLUGINS_DIRr   sortediterdirr   
startswithr'   addappendr    r,   )seendirschilduser_dirs       r   _iter_provider_dirsr;   _   s    D#%D !-5578 	-E<<>UZZ%:%::%FM)113HHUZZ KKU+,	- %&HH,,./ 	-E<<>UZZ%:%::%FzzT!(/KKU+,	- Kr   c                    t         | z  }|j                         r|dz  j                         r|S t               }|r"|| z  }|j                         rt	        |      r|S y)z^Resolve a provider name to its directory.

    Checks bundled first, then user-installed.
    r"   N)r1   r   r'   r    r,   )r   bundledr:   users       r   find_provider_dirr?      sZ      $&G~~W}4<<>$&H$;;=248Kr   c                    g } t               D ]  \  }}d}|dz  }|j                         rB	 ddl}t        |d      5 }|j	                  |      xs i }ddd       j                  dd      }d}	 t        |      }	|	r|	j                         }nd	}| j                  |||f        | S # 1 sw Y   VxY w# t        $ r Y Rw xY w# t        $ r d	}Y @w xY w)
uK  Scan bundled and user-installed directories for available providers.

    Returns list of (name, description, is_available) tuples. May be empty —
    the built-in is core, not discovered here, so a fresh checkout with no
    bundled non-default provider returns []. Bundled providers take precedence
    on name collisions.
     zplugin.yamlr   Nz	utf-8-sig)encodingdescriptionTF)
r;   r'   yamlopen	safe_loadgetr   _load_provider_from_diris_availabler6   )
resultsr   r9   desc	yaml_filerD   fmeta	availableproviders
             r   discover_cron_schedulersrQ      s     G*, 0eM)	)k: 3a>>!,2D3xxr2
 		.u5H$113	!	 	dI./104 N'3 3    	I	s:   B3B'B3/ C'B0	,B33	B?>B?CCc                    t        |       }|st        j                  d|        y	 t        |      }|r|S t        j	                  d|        y# t
        $ r!}t        j	                  d| |       Y d}~yd}~ww xY w)a-  Load and return a CronScheduler instance by name.

    Checks both bundled (``plugins/cron_providers/<name>/``) and user-installed
    (``$HERMES_HOME/plugins/<name>/``) directories. Bundled takes precedence
    on name collisions.

    Returns None if the provider is not found or fails to load.
    z7Cron provider '%s' not found in bundled or user pluginsNz8Cron provider '%s' loaded but no provider instance foundz%Failed to load cron provider '%s': %s)r?   loggerdebugrH   warningr   )r   provider_dirrP   es       r   load_cron_schedulerrX      sr     %T*LNPTU*<8OQSWX >aHs   A A 	A6A11A6c                *	   | j                   }t        | j                  v xs | j                  t        k(  }|rd| n
t         d| }| dz  }|j                         syt        j                  j                  |      }|t        |dd      r|}ndD ]  }|t        j                  vst        t              j                  }|dk(  r|j                  }|dz  }	|	j                         sVt        j                  j                  |t        |	      t        |      g      }
|
st        j                  j!                  |
      }|t        j                  |<   	 |
j"                  j%                  |        |st)        t        g        t        j                  j                  |t        |      t        |       g      }
|
syt        j                  j!                  |
      }|t        j                  |<   g }| j+                  d	      D ]  }|j                   dk(  r|j,                  }| d| }|t        j                  vs9t        j                  j                  |t        |            }|set        j                  j!                  |      }|t        j                  |<   	 |j"                  j%                  |       |j/                  ||f        	 |
j"                  j%                  |       |j7                  dd      \  }}t        j                  j                  |      }|t9        |||       |D ]  \  }}t9        |||        t;        |d      r5t=               }	 |j?                  |       |j@                  r|j@                  S 	 ddl!m"} tG        |      D ];  }t        ||d      }tI        |tJ              s!tM        ||      s.||us3	  |       c S  y# t&        $ r Y Hw xY w# t&        $ r"}t0        j3                  d
||       Y d}~d}~ww xY w# t&        $ rA}t0        j3                  d||       t        j                  j5                  |d       Y d}~yd}~ww xY w# t&        $ r"}t0        j3                  d||       Y d}~d}~ww xY w# t&        $ r Y w xY w)u   Import a provider module and extract the CronScheduler instance.

    The module must have either:
    - A register(ctx) function (plugin-style) — we simulate a ctx
    - A top-level class that extends CronScheduler — we instantiate it
    zplugins.cron_providers.r/   r"   N__file__)r   zplugins.cron_providersr   )r   z*.pyzFailed to load submodule %s: %szFailed to exec_module %s: %s   registerzregister() failed for %s: %sr   )r&   )'r   r1   parentsparent_USER_NAMESPACEr'   r   r   rG   getattrr   rZ   r   r   spec_from_file_locationstrr   loaderexec_moduler   r   globstemr6   rS   rT   poprsplitsetattrhasattr_ProviderCollectorr\   rP   cron.scheduler_providerr&   dir
isinstancetype
issubclass)rV   r   _is_bundledmodule_namer*   cachedmodr^   parent_pathparent_initr   
parent_modloaded_submodulessub_filesub_namefull_sub_namesub_specsub_modrW   parent_name
child_name	collectorr&   	attr_nameattrs                            r   rH   rH      s;    D $|';';;g|?R?RVg?gK6A+D62/IZZ[\`[aGbK},I [[__[)Fgfj$? < 	!FS[[("8n33Y&"-"4"4K)M9%%'$>>AAK 0474D3E B D %.^^%D%DT%J
.8F+! KK33J?	!* '< ~~55Y(+L(9': 6 
 nn--d3#&K  %))&1 	ZH}}-}}H*m1XJ7MCKK/$>>AA!3x= 'nn==hGG18CKK.Z 33G<)00(G1DE	Z$	KK##C( #."4"4S!"<Z[[__[1
!J
C0!2 	,HgC7+	, sJ&(		BLL#!! ))) " 6X 	sIt,tT"z$'F-v ]  ) ! !J % Z%FWXYYZ
  	LL7aHKKOOK.	,  	BLL7qAA	B  sl   O.OP
 (Q R	OO	P%PP
	Q7QQ	R Q==R	RRc                  4    e Zd ZdZd Zd Zd Zd Zd Zd Z	y)	rk   z@Fake plugin context that captures register_cron_scheduler calls.c                    d | _         y NrP   )selfs    r   __init__z_ProviderCollector.__init__S  s	    r   c                    || _         y r   r   )r   rP   s     r   r%   z*_ProviderCollector.register_cron_schedulerV  s	     r   c                     y r    r   argskwargss      r   register_toolz _ProviderCollector.register_toolZ      r   c                     y r   r   r   s      r   register_hookz _ProviderCollector.register_hook]  r   r   c                     y r   r   r   s      r   register_memory_providerz+_ProviderCollector.register_memory_provider`  r   r   c                     y r   r   r   s      r   register_cli_commandz'_ProviderCollector.register_cli_commandc  r   r   N)
__name__
__module____qualname____doc__r   r%   r   r   r   r   r   r   r   rk   rk   P  s#    J!r   rk   )r   rb   r   z	List[str]returnNone)r   Optional[Path])r)   r   r   bool)r   zList[Tuple[str, Path]])r   rb   r   r   )r   zList[Tuple[str, str, bool]])r   rb   r   Optional['CronScheduler'])rV   r   r   r   )r   
__future__r   r   importlib.machineryimportlib.utilloggingr   pathlibr   typingr   r   r   	getLoggerr   rS   rZ   r^   r1   r_   r   r    r,   r;   r?   rQ   rX   rH   rk   r   r   r   <module>r      s   6 #     
  ( (			8	$N))  &>, D,$N2vr r   