MCPcopy Create free account
hub / github.com/WJX20/claude-code / installResolvedPlugin

Function installResolvedPlugin

src/utils/plugins/pluginInstallationHelpers.ts:348–481  ·  view source on GitHub ↗
({
  pluginId,
  entry,
  scope,
  marketplaceInstallLocation,
}: {
  pluginId: string
  entry: PluginMarketplaceEntry
  scope: 'user' | 'project' | 'local'
  marketplaceInstallLocation?: string
})

Source from the content-addressed store, hash-verified

346 * (from a prior marketplace search) to avoid a redundant lookup.
347 */
348export async function installResolvedPlugin({
349 pluginId,
350 entry,
351 scope,
352 marketplaceInstallLocation,
353}: {
354 pluginId: string
355 entry: PluginMarketplaceEntry
356 scope: 'user' | 'project' | 'local'
357 marketplaceInstallLocation?: string
358}): Promise<InstallCoreResult> {
359 const settingSource = scopeToSettingSource(scope)
360
361 // ── Policy guard ──
362 // Org-blocked plugins (managed-settings.json enabledPlugins: false) cannot
363 // be installed. Checked here so all install paths (CLI, UI, hint-triggered)
364 // are covered in one place.
365 if (isPluginBlockedByPolicy(pluginId)) {
366 return { ok: false, reason: 'blocked-by-policy', pluginName: entry.name }
367 }
368
369 // ── Resolve dependency closure ──
370 // depInfo caches marketplace lookups so the materialize loop doesn't
371 // re-fetch. Seed the root if the caller gave us its install location.
372 const depInfo = new Map<
373 string,
374 { entry: PluginMarketplaceEntry; marketplaceInstallLocation: string }
375 >()
376 // Without this guard, a local-source root with undefined
377 // marketplaceInstallLocation falls through: depInfo isn't seeded, the
378 // materialize loop's `if (!info) continue` skips the root, and the user
379 // sees "Successfully installed" while nothing is cached.
380 if (isLocalPluginSource(entry.source) && !marketplaceInstallLocation) {
381 return {
382 ok: false,
383 reason: 'local-source-no-location',
384 pluginName: entry.name,
385 }
386 }
387 if (marketplaceInstallLocation) {
388 depInfo.set(pluginId, { entry, marketplaceInstallLocation })
389 }
390
391 const rootMarketplace = parsePluginIdentifier(pluginId).marketplace
392 const allowedCrossMarketplaces = new Set(
393 (rootMarketplace
394 ? (await getMarketplaceCacheOnly(rootMarketplace))
395 ?.allowCrossMarketplaceDependenciesOn
396 : undefined) ?? [],
397 )
398 const resolution = await resolveDependencyClosure(
399 pluginId,
400 async id => {
401 if (depInfo.has(id)) return depInfo.get(id)!.entry
402 if (id === pluginId) return entry
403 const info = await getPluginById(id)
404 if (info) depInfo.set(id, info)
405 return info?.entry ?? null

Callers 2

installPluginOpFunction · 0.85

Calls 15

scopeToSettingSourceFunction · 0.85
isPluginBlockedByPolicyFunction · 0.85
isLocalPluginSourceFunction · 0.85
parsePluginIdentifierFunction · 0.85
getMarketplaceCacheOnlyFunction · 0.85
resolveDependencyClosureFunction · 0.85
getPluginByIdFunction · 0.85
updateSettingsForSourceFunction · 0.85
getSettingsForSourceFunction · 0.85
getCwdFunction · 0.85
validatePathWithinBaseFunction · 0.85

Tested by

no test coverage detected