Download src/services/plugins/PluginInstallationManager.ts from chenbhao/codev: direct link, hf CLI and curl.
- Browser
- Download file 6.02 kB
-
https://huggingface.co/chenbhao/codev/resolve/main/src/services/plugins/PluginInstallationManager.ts
- Command line
-
hf download hf://chenbhao/codev/src/services/plugins/PluginInstallationManager.ts
-
curl -L -o PluginInstallationManager.ts https://huggingface.co/chenbhao/codev/resolve/main/src/services/plugins/PluginInstallationManager.ts
6.02 kB
| /** | |
| * Background plugin and marketplace installation manager | |
| * | |
| * This module handles automatic installation of plugins and marketplaces | |
| * from trusted sources (repository and user settings) without blocking startup. | |
| */ | |
| import type { AppState } from '../../state/AppState.js' | |
| import { logForDebugging } from '../../utils/debug.js' | |
| import { logForDiagnosticsNoPII } from '../../utils/diagLogs.js' | |
| import { logError } from '../../utils/log.js' | |
| import { | |
| clearMarketplacesCache, | |
| getDeclaredMarketplaces, | |
| loadKnownMarketplacesConfig, | |
| } from '../../utils/plugins/marketplaceManager.js' | |
| import { clearPluginCache } from '../../utils/plugins/pluginLoader.js' | |
| import { | |
| diffMarketplaces, | |
| reconcileMarketplaces, | |
| } from '../../utils/plugins/reconciler.js' | |
| import { refreshActivePlugins } from '../../utils/plugins/refresh.js' | |
| import { logEvent } from '../analytics/index.js' | |
| type SetAppState = (f: (prevState: AppState) => AppState) => void | |
| /** | |
| * Update marketplace installation status in app state | |
| */ | |
| function updateMarketplaceStatus( | |
| setAppState: SetAppState, | |
| name: string, | |
| status: 'pending' | 'installing' | 'installed' | 'failed', | |
| error?: string, | |
| ): void { | |
| setAppState(prevState => ({ | |
| ...prevState, | |
| plugins: { | |
| ...prevState.plugins, | |
| installationStatus: { | |
| ...prevState.plugins.installationStatus, | |
| marketplaces: prevState.plugins.installationStatus.marketplaces.map( | |
| m => (m.name === name ? { ...m, status, error } : m), | |
| ), | |
| }, | |
| }, | |
| })) | |
| } | |
| /** | |
| * Perform background plugin startup checks and installations. | |
| * | |
| * This is a thin wrapper around reconcileMarketplaces() that maps onProgress | |
| * events to AppState updates for the REPL UI. After marketplaces are | |
| * reconciled: | |
| * - New installs → auto-refresh plugins (fixes "plugin-not-found" errors | |
| * from the initial cache-only load on fresh homespace/cleared cache) | |
| * - Updates only → set needsRefresh, show notification for /reload-plugins | |
| */ | |
| export async function performBackgroundPluginInstallations( | |
| setAppState: SetAppState, | |
| ): Promise<void> { | |
| logForDebugging('performBackgroundPluginInstallations called') | |
| try { | |
| // Compute diff upfront for initial UI status (pending spinners) | |
| const declared = getDeclaredMarketplaces() | |
| const materialized = await loadKnownMarketplacesConfig().catch(() => ({})) | |
| const diff = diffMarketplaces(declared, materialized) | |
| const pendingNames = [ | |
| ...diff.missing, | |
| ...diff.sourceChanged.map(c => c.name), | |
| ] | |
| // Initialize AppState with pending status. No per-plugin pending status — | |
| // plugin load is fast (cache hit or local copy); marketplace clone is the | |
| // slow part worth showing progress for. | |
| setAppState(prev => ({ | |
| ...prev, | |
| plugins: { | |
| ...prev.plugins, | |
| installationStatus: { | |
| marketplaces: pendingNames.map(name => ({ | |
| name, | |
| status: 'pending' as const, | |
| })), | |
| plugins: [], | |
| }, | |
| }, | |
| })) | |
| if (pendingNames.length === 0) { | |
| return | |
| } | |
| logForDebugging( | |
| `Installing ${pendingNames.length} marketplace(s) in background`, | |
| ) | |
| const result = await reconcileMarketplaces({ | |
| onProgress: event => { | |
| switch (event.type) { | |
| case 'installing': | |
| updateMarketplaceStatus(setAppState, event.name, 'installing') | |
| break | |
| case 'installed': | |
| updateMarketplaceStatus(setAppState, event.name, 'installed') | |
| break | |
| case 'failed': | |
| updateMarketplaceStatus( | |
| setAppState, | |
| event.name, | |
| 'failed', | |
| event.error, | |
| ) | |
| break | |
| } | |
| }, | |
| }) | |
| const metrics = { | |
| installed_count: result.installed.length, | |
| updated_count: result.updated.length, | |
| failed_count: result.failed.length, | |
| up_to_date_count: result.upToDate.length, | |
| } | |
| logEvent('tengu_marketplace_background_install', metrics) | |
| logForDiagnosticsNoPII( | |
| 'info', | |
| 'tengu_marketplace_background_install', | |
| metrics, | |
| ) | |
| if (result.installed.length > 0) { | |
| // New marketplaces were installed — auto-refresh plugins. This fixes | |
| // "Plugin not found in marketplace" errors from the initial cache-only | |
| // load (e.g., fresh homespace where marketplace cache was empty). | |
| // refreshActivePlugins clears all caches, reloads plugins, and bumps | |
| // pluginReconnectKey so MCP connections are re-established. | |
| clearMarketplacesCache() | |
| logForDebugging( | |
| `Auto-refreshing plugins after ${result.installed.length} new marketplace(s) installed`, | |
| ) | |
| try { | |
| await refreshActivePlugins(setAppState) | |
| } catch (refreshError) { | |
| // If auto-refresh fails, fall back to needsRefresh notification so | |
| // the user can manually run /reload-plugins to recover. | |
| logError(refreshError) | |
| logForDebugging( | |
| `Auto-refresh failed, falling back to needsRefresh: ${refreshError}`, | |
| { level: 'warn' }, | |
| ) | |
| clearPluginCache( | |
| 'performBackgroundPluginInstallations: auto-refresh failed', | |
| ) | |
| setAppState(prev => { | |
| if (prev.plugins.needsRefresh) return prev | |
| return { | |
| ...prev, | |
| plugins: { ...prev.plugins, needsRefresh: true }, | |
| } | |
| }) | |
| } | |
| } else if (result.updated.length > 0) { | |
| // Existing marketplaces updated — notify user to run /reload-plugins. | |
| // Updates are less urgent and the user should choose when to apply them. | |
| clearMarketplacesCache() | |
| clearPluginCache( | |
| 'performBackgroundPluginInstallations: marketplaces reconciled', | |
| ) | |
| setAppState(prev => { | |
| if (prev.plugins.needsRefresh) return prev | |
| return { | |
| ...prev, | |
| plugins: { ...prev.plugins, needsRefresh: true }, | |
| } | |
| }) | |
| } | |
| } catch (error) { | |
| logError(error) | |
| } | |
| } | |