The Plugin Manager tab was the only tab using a custom window.loadPluginsTab()
function with plain fetch() instead of HTMX. This caused a race condition where
plugins_manager.js listened for htmx:afterSwap to initialize, but that event
never fired for the custom fetch. Users had to navigate to a plugin config tab
and back to trigger initialization.
Changes:
- Switch plugins tab to hx-get/hx-trigger="revealed" matching all other tabs
- Remove ~560 lines of dead code (script extraction for a partial with no scripts,
nested retry intervals, inline HTML card rendering fallbacks)
- Add simple loadPluginsDirect() fallback for when HTMX fails to load
- Remove typeof htmx guard on afterSwap listener so it registers unconditionally
- Tighten afterSwap target check to avoid spurious re-init from other tab swaps
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PluginManager thread safety:
- Add RLock protecting plugin_manifests and plugin_directories
- Build scan results locally in _scan_directory_for_plugins, then update
shared state under lock
- Protect reads in get_plugin_info, get_all_plugin_info,
get_plugin_directory, get_plugin_display_modes, find_plugin_for_mode
- Protect manifest mutation in reload_plugin
- Prevents races between background reconciliation thread and request
handlers reading plugin state
Reconciliation retry:
- Clear _reconciliation_started on exception so next request retries
- Check result.reconciliation_successful before marking done
- Reset _reconciliation_started on non-success results to allow retry
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Move backup-folder filter from _get_config_state to _get_disk_state
where the artifact actually lives
- Run startup reconciliation in a background thread so requests aren't
blocked by plugin reinstallation
- Set _reconciliation_done only after success so failures allow retries
- Replace print() with proper logger in reconciliation
- Wrap load_schema in try/except so exceptions fall through to
conservative masking instead of 500
- Handle list values in _conservative_mask_config for nested secrets
- Remove duplicate import re
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The store registry uses unprefixed IDs (e.g., 'weather') while older
installs used prefixed config keys (e.g., 'ledmatrix-weather'). Both
update_plugin() and auto-repair now try the unprefixed ID as a fallback
when the prefixed one isn't found in the registry.
Also filters system config keys (schedule, display, etc.) from
reconciliation to avoid false positives.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Plugins whose directories are missing (failed update, migration, etc.)
now get automatically reinstalled from the store on startup. The config
endpoint no longer returns a hard 500 when a schema is unavailable —
it falls back to conservative key-name-based masking so the settings
page stays functional.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>