mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-10-04 14:25:08 +00:00
refactor(web): read plugins through a PluginCatalog; only the display runs them (#688)
The web process built its own PluginManager and loaded plugins into itself: store installs and updates loaded or reloaded a web-side copy, and config saves and enable/disable called on_config_change, on_enable and on_disable on it. None of that reached the panel, and /plugins/installed reported runtime state from those copies. - Add PluginCatalog (src/plugin_system/plugin_catalog.py): manifests, directories, display modes, installed version, schema and config reads, with no way to run a plugin. app.py and both blueprints use it; the plugin_manager blueprint attribute is gone. - Remove every lifecycle call from the web routes. Config changes already reach the display through ConfigService (on_config_change) and the enabled-set reconcile. - Health and metrics readers move to api_v3.health_tracker / resource_monitor. /plugins/installed reports loaded/state/error_info as null (the display does not publish them) and enabled by the display's rule. - Store install, update and uninstall answer restart_required when the running display will not pick the change up by itself (display_restart_required). The restart banner follows the flag via window.noteRestartRequired instead of the /config/main URL heuristic; /config/main now sends restart_required: true. - The one remaining in-process import of plugin code (Starlark helper modules, oauth_flow action scripts) goes through _import_plugin_code_in_web_process() until a web-entry contract. - /plugins/installed reports vegas_participation (from #682) from the user's setting or the manifest, with vegas_participation_source; when only the plugin's code decides it, null with source 'runtime', since the web process no longer has plugin instances to ask. - Check & Update All keeps its restart flags when the final list refresh fails, and asks for a restart when an enabled plugin's first request got no answer and the re-sent one found it up to date. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+27
-19
@@ -34,7 +34,7 @@ from src.common.path_safety import (
|
||||
)
|
||||
from werkzeug.exceptions import HTTPException
|
||||
from src.exceptions import ConfigError
|
||||
from src.plugin_system.plugin_manager import PluginManager
|
||||
from src.plugin_system.plugin_catalog import PluginCatalog
|
||||
from src.plugin_system.store_manager import PluginStoreManager
|
||||
from src.plugin_system.saved_repositories import SavedRepositoriesManager
|
||||
from src.plugin_system.schema_manager import SchemaManager
|
||||
@@ -117,12 +117,6 @@ else:
|
||||
# If relative, resolve relative to the project root
|
||||
plugins_dir = project_root / plugins_dir_name
|
||||
|
||||
plugin_manager = PluginManager(
|
||||
plugins_dir=str(plugins_dir),
|
||||
config_manager=config_manager,
|
||||
display_manager=None, # Not needed for web interface
|
||||
cache_manager=None # Not needed for web interface
|
||||
)
|
||||
plugin_store_manager = PluginStoreManager(plugins_dir=str(plugins_dir))
|
||||
# A core `git pull` update (or any checkout) restores built-in plugins
|
||||
# committed under plugin-repos/, even ones the user uninstalled. Re-remove any
|
||||
@@ -149,6 +143,18 @@ schema_manager = SchemaManager(
|
||||
config_manager=config_manager
|
||||
)
|
||||
|
||||
# The web process reads plugins as files and never runs them: no plugin module
|
||||
# is imported, no plugin class instantiated, no lifecycle hook called here.
|
||||
# Only the display process (src/display_controller.py) does that. Config
|
||||
# saves reach the running plugins through the display's config watcher; what
|
||||
# the display knows at run time (health, metrics, errors, current mode) it
|
||||
# publishes to the shared cache. See docs/ARCHITECTURE.md.
|
||||
plugin_catalog = PluginCatalog(
|
||||
plugins_dir=plugins_dir,
|
||||
config_manager=config_manager,
|
||||
schema_manager=schema_manager,
|
||||
)
|
||||
|
||||
# Initialize operation queue for plugin operations
|
||||
operation_queue = PluginOperationQueue(max_history=500)
|
||||
|
||||
@@ -169,7 +175,7 @@ operation_history = OperationHistory(
|
||||
)
|
||||
|
||||
# Plugin discovery is deferred until first API request that needs it
|
||||
# This improves startup time - endpoints will call discover_plugins() when needed
|
||||
# This improves startup time - endpoints call plugin_catalog.discover_plugins() when needed
|
||||
|
||||
# Register blueprints
|
||||
from web_interface.blueprints.pages_v3 import pages_v3
|
||||
@@ -177,13 +183,13 @@ from web_interface.blueprints.api_v3 import api_v3
|
||||
|
||||
# Initialize managers in blueprints
|
||||
pages_v3.config_manager = config_manager
|
||||
pages_v3.plugin_manager = plugin_manager
|
||||
pages_v3.plugin_catalog = plugin_catalog
|
||||
pages_v3.plugin_store_manager = plugin_store_manager
|
||||
pages_v3.saved_repositories_manager = saved_repositories_manager
|
||||
pages_v3.schema_manager = schema_manager
|
||||
|
||||
api_v3.config_manager = config_manager
|
||||
api_v3.plugin_manager = plugin_manager
|
||||
api_v3.plugin_catalog = plugin_catalog
|
||||
api_v3.plugin_store_manager = plugin_store_manager
|
||||
api_v3.saved_repositories_manager = saved_repositories_manager
|
||||
api_v3.schema_manager = schema_manager
|
||||
@@ -194,17 +200,19 @@ api_v3.operation_history = operation_history
|
||||
from src.cache_manager import CacheManager
|
||||
api_v3.cache_manager = CacheManager()
|
||||
|
||||
# Wire plugin health/metrics for the web process. The display service records
|
||||
# health and execution-time metrics to the shared on-disk cache; giving the web
|
||||
# process its own tracker/monitor backed by that same cache lets the health API
|
||||
# routes (/api/v3/plugins/health, /plugins/metrics) read that persisted data.
|
||||
# Plugin health and metrics as the display publishes them. The display service
|
||||
# records health and execution-time metrics to the shared on-disk cache; a
|
||||
# tracker/monitor backed by that same cache lets the health API routes
|
||||
# (/api/v3/plugins/health, /plugins/metrics) read what it wrote.
|
||||
# Guarded so any init failure degrades to "not available" rather than breaking
|
||||
# the web server.
|
||||
api_v3.health_tracker = None
|
||||
api_v3.resource_monitor = None
|
||||
try:
|
||||
from src.plugin_system.plugin_health import PluginHealthTracker
|
||||
from src.plugin_system.resource_monitor import PluginResourceMonitor
|
||||
plugin_manager.health_tracker = PluginHealthTracker(api_v3.cache_manager)
|
||||
plugin_manager.resource_monitor = PluginResourceMonitor(api_v3.cache_manager)
|
||||
api_v3.health_tracker = PluginHealthTracker(api_v3.cache_manager)
|
||||
api_v3.resource_monitor = PluginResourceMonitor(api_v3.cache_manager)
|
||||
except Exception as _hm_err: # pragma: no cover - defensive startup guard
|
||||
logging.getLogger(__name__).warning(
|
||||
"Could not enable plugin health/metrics for web UI: %s", _hm_err
|
||||
@@ -960,7 +968,7 @@ def _run_startup_reconciliation() -> None:
|
||||
reconciler = StateReconciliation(
|
||||
state_manager=plugin_state_manager,
|
||||
config_manager=config_manager,
|
||||
plugin_manager=plugin_manager,
|
||||
plugin_manager=plugin_catalog,
|
||||
plugins_dir=plugins_dir,
|
||||
store_manager=plugin_store_manager
|
||||
)
|
||||
@@ -968,7 +976,7 @@ def _run_startup_reconciliation() -> None:
|
||||
if result.inconsistencies_found:
|
||||
_logger.info("[Reconciliation] %s", result.message)
|
||||
if result.inconsistencies_fixed:
|
||||
plugin_manager.discover_plugins()
|
||||
plugin_catalog.discover_plugins()
|
||||
if not result.reconciliation_successful:
|
||||
_logger.warning(
|
||||
"[Reconciliation] Finished with %d unresolved issue(s); "
|
||||
@@ -1043,7 +1051,7 @@ def start_auto_update_scheduler():
|
||||
config_manager=config_manager,
|
||||
core_update=perform_core_update,
|
||||
store_manager=plugin_store_manager,
|
||||
plugin_manager=plugin_manager,
|
||||
plugin_catalog=plugin_catalog,
|
||||
schema_manager=schema_manager,
|
||||
operation_history=operation_history,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user