import { createHash } from "node:crypto";
import type { Dirent } from "node:fs";
import fs from "node:fs/promises";
import path from "node:path";
import { satisfiesPluginApiRange } from "../infra/clawhub.js";
import { packageNameMatchesId } from "../infra/install-safe-path.js";
import {
  resolveNpmPackArchiveMetadata,
  resolveNpmSpecMetadata,
  createNpmMetadataEnv,
  type NpmIntegrityDrift,
  type NpmSpecResolution,
} from "../infra/install-source-utils.js";
import { resolveNpmIntegrityDriftWithDefaultMessage } from "../infra/npm-integrity.js";
import {
  type ManagedNpmRootPeerDependencySnapshot,
  readManagedNpmRootInstalledDependency,
  readManagedNpmRootPeerDependencySnapshot,
  readOpenClawManagedNpmRootOverrides,
  repairManagedNpmRootOpenClawPeer,
  removeManagedNpmRootDependency,
  resolveManagedNpmRootDependencySpec,
  restoreManagedNpmRootPeerDependencySnapshot,
  syncManagedNpmRootPeerDependencies,
  upsertManagedNpmRootDependency,
  type ManagedNpmRootInstalledDependency,
} from "../infra/npm-managed-root.js";
import {
  compareOpenClawReleaseVersions,
  formatPrereleaseResolutionError,
  isExactSemverVersion,
  isPrereleaseSemverVersion,
  isPrereleaseResolutionAllowed,
  parseRegistryNpmSpec,
  validateRegistryNpmSpec,
  type ParsedRegistryNpmSpec,
} from "../infra/npm-registry-spec.js";
import { installedPackageNeedsOpenClawPeerLinkRepair } from "../infra/package-update-utils.js";
import {
  createSafeNpmInstallArgs,
  createSafeNpmInstallEnv,
} from "../infra/safe-package-install.js";
import { compareComparableSemver, parseComparableSemver } from "../infra/semver-compare.js";
import { runCommandWithTimeout } from "../process/exec.js";
import { createLazyImportLoader } from "../shared/lazy-promise.js";
import { normalizeOptionalString } from "../shared/string-coerce.js";
import { resolveUserPath } from "../utils.js";
import {
  encodePluginInstallDirName,
  matchesExpectedPluginId,
  resolveDefaultPluginExtensionsDir,
  resolveDefaultPluginNpmDir,
  resolvePluginNpmProjectDir,
  safePluginInstallFileName,
  validatePluginId,
} from "./install-paths.js";
import type { InstallSecurityScanResult } from "./install-security-scan.js";
import type { InstallSafetyOverrides } from "./install-security-scan.js";
import {
  resolvePackageExtensionEntries,
  type OpenClawPackageManifest,
  type PackageManifest as PluginPackageManifest,
} from "./manifest.js";
import { resolvePackagePluginApiRange } from "./package-compat.js";
import { validatePackageExtensionEntriesForInstall } from "./package-entry-resolution.js";
import {
  linkOpenClawPeerDependencies,
  relinkOpenClawPeerDependenciesInManagedNpmRoot,
} from "./plugin-peer-link.js";

export { resolvePluginInstallDir } from "./install-paths.js";

const pluginInstallRuntimeLoader = createLazyImportLoader(() => import("./install.runtime.js"));

async function loadPluginInstallRuntime() {
  return await pluginInstallRuntimeLoader.load();
}

type PluginInstallLogger = {
  info?: (message: string) => void;
  warn?: (message: string) => void;
};

type PackageManifest = PluginPackageManifest & {
  dependencies?: Record<string, string>;
  optionalDependencies?: Record<string, string>;
  peerDependencies?: Record<string, string>;
};
type PluginInstallRuntime = Awaited<ReturnType<typeof loadPluginInstallRuntime>>;

function formatUnresolvedOpenClawPeerLinkError(packageName: string): string {
  return `Installed plugin ${packageName} declares openclaw as a peer dependency, but OpenClaw could not create a plugin-local node_modules/openclaw link. Run from a packaged OpenClaw install or reinstall OpenClaw, then retry.`;
}

function isNpmAliasOverrideComparatorError(result: { stdout: string; stderr: string }): boolean {
  return `${result.stderr}\n${result.stdout}`.includes("Invalid comparator: npm:");
}

const MISSING_EXTENSIONS_ERROR =
  'package.json missing openclaw.extensions; update the plugin package to include openclaw.extensions (for example ["./dist/index.js"]). See https://docs.openclaw.ai/help/troubleshooting#plugin-install-fails-with-missing-openclaw-extensions';
const PLUGIN_ARCHIVE_ROOT_MARKERS = [
  "package.json",
  "openclaw.plugin.json",
  ".codex-plugin/plugin.json",
  ".claude-plugin/plugin.json",
  ".cursor-plugin/plugin.json",
];
const MANAGED_NPM_PACK_ARCHIVE_DIR = "_openclaw-pack-archives";

export const PLUGIN_INSTALL_ERROR_CODE = {
  INVALID_NPM_SPEC: "invalid_npm_spec",
  INVALID_MIN_HOST_VERSION: "invalid_min_host_version",
  UNKNOWN_HOST_VERSION: "unknown_host_version",
  INCOMPATIBLE_HOST_VERSION: "incompatible_host_version",
  INCOMPATIBLE_PLUGIN_API: "incompatible_plugin_api",
  INVALID_PLUGIN_API: "invalid_plugin_api",
  MISSING_OPENCLAW_EXTENSIONS: "missing_openclaw_extensions",
  MISSING_PLUGIN_MANIFEST: "missing_plugin_manifest",
  EMPTY_OPENCLAW_EXTENSIONS: "empty_openclaw_extensions",
  INVALID_OPENCLAW_EXTENSIONS: "invalid_openclaw_extensions",
  NPM_PACKAGE_NOT_FOUND: "npm_package_not_found",
  PLUGIN_ID_MISMATCH: "plugin_id_mismatch",
  SECURITY_SCAN_BLOCKED: "security_scan_blocked",
  SECURITY_SCAN_FAILED: "security_scan_failed",
} as const;

export type PluginInstallErrorCode =
  (typeof PLUGIN_INSTALL_ERROR_CODE)[keyof typeof PLUGIN_INSTALL_ERROR_CODE];

export type InstallPluginResult =
  | {
      ok: true;
      pluginId: string;
      targetDir: string;
      manifestName?: string;
      version?: string;
      extensions: string[];
      npmResolution?: NpmSpecResolution;
      integrityDrift?: NpmIntegrityDrift;
    }
  | { ok: false; error: string; code?: PluginInstallErrorCode };

type PluginInstallFailureResult = Extract<InstallPluginResult, { ok: false }>;

function validateOpenClawPackageCompatibility(params: {
  pluginId: string;
  currentHostVersion: string;
  packageMetadata?: OpenClawPackageManifest;
}): PluginInstallFailureResult | null {
  const pluginApiRangeCheck = resolvePackagePluginApiRange(params.packageMetadata);
  if (!pluginApiRangeCheck.ok) {
    return {
      ok: false,
      error: `invalid package.json openclaw.compat.pluginApi: ${pluginApiRangeCheck.error}`,
      code: PLUGIN_INSTALL_ERROR_CODE.INVALID_PLUGIN_API,
    };
  }
  const pluginApiRange = pluginApiRangeCheck.range;
  if (pluginApiRange && !satisfiesPluginApiRange(params.currentHostVersion, pluginApiRange)) {
    return {
      ok: false,
      error: `plugin "${params.pluginId}" requires plugin API ${pluginApiRange}, but this OpenClaw runtime exposes ${params.currentHostVersion}. Upgrade OpenClaw or install a compatible plugin version and retry.`,
      code: PLUGIN_INSTALL_ERROR_CODE.INCOMPATIBLE_PLUGIN_API,
    };
  }

  return null;
}

function validateOpenClawPackageInstallCompatibility(params: {
  runtime: PluginInstallRuntime;
  pluginId: string;
  packageMetadata?: OpenClawPackageManifest;
}): PluginInstallFailureResult | null {
  const currentHostVersion = params.runtime.resolveCompatibilityHostVersion();
  const minHostVersionCheck = params.runtime.checkMinHostVersion({
    currentVersion: currentHostVersion,
    minHostVersion: params.packageMetadata?.install?.minHostVersion,
  });
  if (!minHostVersionCheck.ok) {
    if (minHostVersionCheck.kind === "invalid") {
      return {
        ok: false,
        error: `invalid package.json openclaw.install.minHostVersion: ${minHostVersionCheck.error}`,
        code: PLUGIN_INSTALL_ERROR_CODE.INVALID_MIN_HOST_VERSION,
      };
    }
    if (minHostVersionCheck.kind === "unknown_host_version") {
      return {
        ok: false,
        error: `plugin "${params.pluginId}" requires OpenClaw >=${minHostVersionCheck.requirement.minimumLabel}, but this host version could not be determined. Re-run from a released build or set OPENCLAW_VERSION and retry.`,
        code: PLUGIN_INSTALL_ERROR_CODE.UNKNOWN_HOST_VERSION,
      };
    }
    return {
      ok: false,
      error: `plugin "${params.pluginId}" requires OpenClaw >=${minHostVersionCheck.requirement.minimumLabel}, but this host is ${minHostVersionCheck.currentVersion}. Upgrade OpenClaw and retry.`,
      code: PLUGIN_INSTALL_ERROR_CODE.INCOMPATIBLE_HOST_VERSION,
    };
  }

  return validateOpenClawPackageCompatibility({
    pluginId: params.pluginId,
    currentHostVersion,
    packageMetadata: params.packageMetadata,
  });
}

async function readOptionalPackageManifest(params: {
  runtime: PluginInstallRuntime;
  packageDir: string;
}): Promise<{ ok: true; manifest?: PackageManifest } | PluginInstallFailureResult> {
  const manifestPath = path.join(params.packageDir, "package.json");
  if (!(await params.runtime.fileExists(manifestPath))) {
    return { ok: true };
  }

  try {
    return {
      ok: true,
      manifest: await params.runtime.readJsonFile<PackageManifest>(manifestPath),
    };
  } catch (err) {
    return { ok: false, error: `invalid package.json: ${String(err)}` };
  }
}

export type PluginNpmIntegrityDriftParams = {
  spec: string;
  expectedIntegrity: string;
  actualIntegrity: string;
  resolution: NpmSpecResolution;
};

type PluginInstallPolicyRequest = {
  kind: "plugin-dir" | "plugin-archive" | "plugin-file" | "plugin-npm" | "plugin-git";
  requestedSpecifier?: string;
};

const defaultLogger: PluginInstallLogger = {};

function ensureOpenClawExtensions(params: { manifest: PackageManifest }):
  | {
      ok: true;
      entries: string[];
    }
  | {
      ok: false;
      error: string;
      code: PluginInstallErrorCode;
    } {
  const resolved = resolvePackageExtensionEntries(params.manifest);
  if (resolved.status === "missing") {
    return {
      ok: false,
      error: MISSING_EXTENSIONS_ERROR,
      code: PLUGIN_INSTALL_ERROR_CODE.MISSING_OPENCLAW_EXTENSIONS,
    };
  }
  if (resolved.status === "empty") {
    return {
      ok: false,
      error: "package.json openclaw.extensions is empty",
      code: PLUGIN_INSTALL_ERROR_CODE.EMPTY_OPENCLAW_EXTENSIONS,
    };
  }
  if (resolved.status === "invalid") {
    return {
      ok: false,
      error: resolved.error,
      code: PLUGIN_INSTALL_ERROR_CODE.INVALID_OPENCLAW_EXTENSIONS,
    };
  }
  return {
    ok: true,
    entries: resolved.entries,
  };
}

function isNpmPackageNotFoundMessage(error: string): boolean {
  const normalized = error.trim();
  if (normalized.startsWith("Package not found on npm:")) {
    return true;
  }
  return /E404|404 not found|not in this registry/i.test(normalized);
}

function compareNpmSemver(a: string, b: string): number {
  const releaseCmp = compareOpenClawReleaseVersions(a, b);
  if (releaseCmp !== null) {
    return releaseCmp;
  }
  return compareComparableSemver(parseComparableSemver(a), parseComparableSemver(b)) ?? 0;
}

type TrustedOfficialPrereleaseResolution =
  | { kind: "stable"; resolution: NpmSpecResolution }
  | { kind: "prerelease-only"; resolution: NpmSpecResolution }
  | { kind: "allow-prerelease-only" };

async function loadNpmPackageVersions(params: {
  packageName: string;
  timeoutMs: number;
}): Promise<string[] | null> {
  const versions = await runCommandWithTimeout(
    ["npm", "view", params.packageName, "versions", "--json"],
    {
      timeoutMs: Math.max(params.timeoutMs, 60_000),
      env: createNpmMetadataEnv(),
    },
  );
  if (versions.code !== 0) {
    return null;
  }

  let parsed: unknown;
  try {
    parsed = JSON.parse(versions.stdout.trim());
  } catch {
    return null;
  }
  return (Array.isArray(parsed) ? parsed : [parsed]).filter(
    (value): value is string => typeof value === "string" && isExactSemverVersion(value),
  );
}

async function resolveTrustedOfficialPrereleaseResolution(params: {
  spec: ParsedRegistryNpmSpec;
  resolvedPrereleaseVersion: string;
  timeoutMs: number;
  logger: PluginInstallLogger;
}): Promise<TrustedOfficialPrereleaseResolution | null> {
  if (!params.spec.name.startsWith("@openclaw/")) {
    return null;
  }
  const semverVersions = await loadNpmPackageVersions({
    packageName: params.spec.name,
    timeoutMs: params.timeoutMs,
  });
  if (!semverVersions) {
    return null;
  }
  const stableVersion = semverVersions
    .filter((value) => !isPrereleaseSemverVersion(value))
    .toSorted(compareNpmSemver)
    .at(-1);
  if (!stableVersion) {
    const prereleaseVersion = semverVersions
      .filter(isPrereleaseSemverVersion)
      .toSorted(compareNpmSemver)
      .at(-1);
    if (prereleaseVersion && semverVersions.every(isPrereleaseSemverVersion)) {
      if (prereleaseVersion !== params.resolvedPrereleaseVersion) {
        const prereleaseSpec = `${params.spec.name}@${prereleaseVersion}`;
        const metadataResult = await resolveNpmSpecMetadata({
          spec: prereleaseSpec,
          timeoutMs: params.timeoutMs,
        });
        if (!metadataResult.ok) {
          return null;
        }
        params.logger.warn?.(
          `Resolved ${params.spec.raw} to prerelease version ${params.resolvedPrereleaseVersion}; using newest prerelease ${prereleaseSpec} because this trusted official OpenClaw package has no stable npm versions yet.`,
        );
        return { kind: "prerelease-only", resolution: metadataResult.metadata };
      }
      params.logger.warn?.(
        `Resolved ${params.spec.raw} to prerelease version ${params.resolvedPrereleaseVersion}; allowing it because this trusted official OpenClaw package has no stable npm versions yet.`,
      );
      return { kind: "allow-prerelease-only" };
    }
    return null;
  }

  const stableSpec = `${params.spec.name}@${stableVersion}`;
  const metadataResult = await resolveNpmSpecMetadata({
    spec: stableSpec,
    timeoutMs: params.timeoutMs,
  });
  if (!metadataResult.ok) {
    return null;
  }
  params.logger.warn?.(
    `Resolved ${params.spec.raw} to prerelease version ${params.resolvedPrereleaseVersion}; falling back to stable ${stableSpec} for this trusted official OpenClaw install.`,
  );
  return { kind: "stable", resolution: metadataResult.metadata };
}

function shouldResolveLatestCompatibleNpmVersion(spec: ParsedRegistryNpmSpec): boolean {
  return (
    spec.selectorKind === "none" ||
    (spec.selectorKind === "tag" && (spec.selector ?? "").toLowerCase() === "latest")
  );
}

function canResolveAroundCompatibilityError(error: PluginInstallFailureResult): boolean {
  return (
    error.code === PLUGIN_INSTALL_ERROR_CODE.INCOMPATIBLE_HOST_VERSION ||
    error.code === PLUGIN_INSTALL_ERROR_CODE.INCOMPATIBLE_PLUGIN_API
  );
}

function validateNpmResolutionCompatibility(params: {
  runtime: PluginInstallRuntime;
  parsedSpec: ParsedRegistryNpmSpec;
  expectedPluginId?: string;
  resolution: NpmSpecResolution;
}): PluginInstallFailureResult | null {
  return validateOpenClawPackageInstallCompatibility({
    runtime: params.runtime,
    pluginId: params.expectedPluginId ?? params.resolution.name ?? params.parsedSpec.name,
    packageMetadata: params.resolution.packageOpenClaw as OpenClawPackageManifest | undefined,
  });
}

async function resolveLatestCompatibleNpmResolution(params: {
  runtime: PluginInstallRuntime;
  parsedSpec: ParsedRegistryNpmSpec;
  expectedPluginId?: string;
  currentResolution: NpmSpecResolution;
  timeoutMs: number;
  logger: PluginInstallLogger;
}): Promise<NpmSpecResolution | null> {
  if (
    !shouldResolveLatestCompatibleNpmVersion(params.parsedSpec) ||
    !params.currentResolution.version
  ) {
    return null;
  }

  const versions = await loadNpmPackageVersions({
    packageName: params.parsedSpec.name,
    timeoutMs: params.timeoutMs,
  });
  if (!versions) {
    return null;
  }

  const currentVersion = params.currentResolution.version;
  const candidates = versions
    .filter((version) => !isPrereleaseSemverVersion(version))
    .filter((version) => compareNpmSemver(version, currentVersion) < 0)
    .toSorted(compareNpmSemver)
    .toReversed();
  for (const version of candidates) {
    const spec = `${params.parsedSpec.name}@${version}`;
    const metadataResult = await resolveNpmSpecMetadata({
      spec,
      timeoutMs: params.timeoutMs,
    });
    if (!metadataResult.ok) {
      params.logger.warn?.(
        `Could not inspect ${spec} while looking for a compatible plugin version: ${metadataResult.error}`,
      );
      continue;
    }
    const compatibilityError = validateNpmResolutionCompatibility({
      runtime: params.runtime,
      parsedSpec: params.parsedSpec,
      expectedPluginId: params.expectedPluginId,
      resolution: metadataResult.metadata,
    });
    if (!compatibilityError) {
      params.logger.warn?.(
        `Resolved ${params.parsedSpec.raw} to ${params.currentResolution.resolvedSpec ?? currentVersion}, but that version is incompatible with this OpenClaw runtime; using newest compatible ${metadataResult.metadata.resolvedSpec ?? spec}.`,
      );
      return metadataResult.metadata;
    }
  }

  return null;
}

function buildFileInstallResult(pluginId: string, targetFile: string): InstallPluginResult {
  return {
    ok: true,
    pluginId,
    targetDir: targetFile,
    manifestName: undefined,
    version: undefined,
    extensions: [path.basename(targetFile)],
  };
}

function buildDirectoryInstallResult(params: {
  pluginId: string;
  targetDir: string;
  manifestName?: string;
  version?: string;
  extensions: string[];
}): InstallPluginResult {
  return {
    ok: true,
    pluginId: params.pluginId,
    targetDir: params.targetDir,
    manifestName: params.manifestName,
    version: params.version,
    extensions: params.extensions,
  };
}

function hasPackageRuntimeDependencies(manifest: PackageManifest): boolean {
  return (
    Object.keys(manifest.dependencies ?? {}).length > 0 ||
    Object.keys(manifest.optionalDependencies ?? {}).length > 0
  );
}

function buildBlockedInstallResult(params: {
  blocked: NonNullable<NonNullable<InstallSecurityScanResult>["blocked"]>;
}): Extract<InstallPluginResult, { ok: false }> {
  return {
    ok: false,
    error: params.blocked.reason,
    ...(params.blocked.code === "security_scan_failed"
      ? { code: PLUGIN_INSTALL_ERROR_CODE.SECURITY_SCAN_FAILED }
      : params.blocked.code === "security_scan_blocked"
        ? { code: PLUGIN_INSTALL_ERROR_CODE.SECURITY_SCAN_BLOCKED }
        : {}),
  };
}

async function rollbackManagedNpmPluginInstall(params: {
  npmRoot: string;
  packageName: string;
  targetDir: string;
  timeoutMs: number;
  logger: PluginInstallLogger;
  peerDependencySnapshot?: ManagedNpmRootPeerDependencySnapshot;
}): Promise<void> {
  try {
    await runCommandWithTimeout(
      [
        "npm",
        "uninstall",
        "--loglevel=error",
        "--legacy-peer-deps",
        "--ignore-scripts",
        "--no-audit",
        "--no-fund",
        params.packageName,
      ],
      {
        cwd: params.npmRoot,
        timeoutMs: Math.max(params.timeoutMs, 300_000),
        env: createSafeNpmInstallEnv(process.env, {
          legacyPeerDeps: true,
          npmConfigCwd: params.npmRoot,
          packageLock: true,
          quiet: true,
        }),
      },
    );
  } catch (error) {
    params.logger.warn?.(
      `Failed to run npm uninstall rollback for ${params.packageName}: ${String(error)}`,
    );
  }
  try {
    await fs.rm(params.targetDir, { recursive: true, force: true });
  } catch (error) {
    params.logger.warn?.(
      `Failed to remove failed plugin install directory ${params.targetDir}: ${String(error)}`,
    );
  }
  try {
    await removeManagedNpmRootDependency({
      npmRoot: params.npmRoot,
      packageName: params.packageName,
    });
  } catch (error) {
    params.logger.warn?.(
      `Failed to remove managed npm dependency ${params.packageName}: ${String(error)}`,
    );
  }
  if (params.peerDependencySnapshot) {
    try {
      const preRestorePeerDependencySnapshot = await readManagedNpmRootPeerDependencySnapshot({
        npmRoot: params.npmRoot,
      });
      const restoredPeerDependencyNames = new Set(
        params.peerDependencySnapshot.managedPeerDependencies,
      );
      const addedPeerDependencyNames =
        preRestorePeerDependencySnapshot.managedPeerDependencies.filter(
          (packageName) => !restoredPeerDependencyNames.has(packageName),
        );
      await restoreManagedNpmRootPeerDependencySnapshot({
        npmRoot: params.npmRoot,
        snapshot: params.peerDependencySnapshot,
      });
      const cleanupResult = await runCommandWithTimeout(
        [
          "npm",
          "install",
          "--omit=dev",
          "--omit=peer",
          "--loglevel=error",
          "--legacy-peer-deps",
          "--ignore-scripts",
          "--no-audit",
          "--no-fund",
        ],
        {
          cwd: params.npmRoot,
          timeoutMs: Math.max(params.timeoutMs, 300_000),
          env: createSafeNpmInstallEnv(process.env, {
            legacyPeerDeps: true,
            npmConfigCwd: params.npmRoot,
            packageLock: true,
            quiet: true,
          }),
        },
      );
      if (cleanupResult.code !== 0) {
        params.logger.warn?.(
          `npm install cleanup after rollback for ${params.packageName} exited ${cleanupResult.code}: ${cleanupResult.stderr.trim() || cleanupResult.stdout.trim()}`,
        );
        await Promise.all(
          addedPeerDependencyNames.map(async (packageName) => {
            try {
              await fs.rm(resolveManagedNpmRootPackageDir(params.npmRoot, packageName), {
                recursive: true,
                force: true,
              });
            } catch (error) {
              params.logger.warn?.(
                `Failed to remove rolled-back managed peer dependency ${packageName}: ${String(error)}`,
              );
            }
          }),
        );
      }
    } catch (error) {
      params.logger.warn?.(
        `Failed to restore managed npm peer dependencies after rollback for ${params.packageName}: ${String(error)}`,
      );
    }
  }
  if (params.packageName !== "openclaw") {
    try {
      await repairManagedNpmRootOpenClawPeer({
        npmRoot: params.npmRoot,
        timeoutMs: params.timeoutMs,
        logger: params.logger,
      });
    } catch (error) {
      params.logger.warn?.(
        `Failed to repair managed npm openclaw peer after rollback: ${String(error)}`,
      );
    }
  }
  try {
    await relinkOpenClawPeerDependenciesInManagedNpmRoot({
      npmRoot: params.npmRoot,
      logger: params.logger,
    });
  } catch (error) {
    params.logger.warn?.(
      `Failed to repair managed npm peer links after rollback for ${params.packageName}: ${String(error)}`,
    );
  }
}

function resolveInstalledNpmResolutionMismatch(params: {
  packageName: string;
  expected: NpmSpecResolution;
  installed: ManagedNpmRootInstalledDependency | null;
}): string | null {
  if (!params.installed) {
    return `npm install did not record package-lock metadata for ${params.packageName}`;
  }
  if (params.expected.version && params.installed.version !== params.expected.version) {
    return `npm install resolved ${params.packageName} to version ${params.installed.version ?? "unknown"}, expected ${params.expected.version}`;
  }
  if (params.expected.integrity && params.installed.integrity !== params.expected.integrity) {
    return `npm install resolved ${params.packageName} with integrity ${params.installed.integrity ?? "unknown"}, expected ${params.expected.integrity}`;
  }
  return null;
}

async function listManagedNpmRootPackageNames(npmRoot: string): Promise<Set<string>> {
  const nodeModulesDir = path.join(npmRoot, "node_modules");
  let entries: Dirent[];
  try {
    entries = await fs.readdir(nodeModulesDir, { withFileTypes: true });
  } catch (error) {
    if ((error as NodeJS.ErrnoException).code === "ENOENT") {
      return new Set();
    }
    throw error;
  }

  const packageNames = new Set<string>();
  for (const entry of entries.toSorted((left, right) => left.name.localeCompare(right.name))) {
    if (entry.name === ".bin" || entry.name === "openclaw") {
      continue;
    }
    if (entry.name.startsWith("@")) {
      const scopeDir = path.join(nodeModulesDir, entry.name);
      let scopedEntries: Dirent[];
      try {
        scopedEntries = await fs.readdir(scopeDir, { withFileTypes: true });
      } catch (error) {
        if ((error as NodeJS.ErrnoException).code === "ENOENT") {
          continue;
        }
        throw error;
      }
      for (const scopedEntry of scopedEntries.toSorted((left, right) =>
        left.name.localeCompare(right.name),
      )) {
        if (scopedEntry.isDirectory() || scopedEntry.isSymbolicLink()) {
          packageNames.add(`${entry.name}/${scopedEntry.name}`);
        }
      }
      continue;
    }
    if (entry.isDirectory() || entry.isSymbolicLink()) {
      packageNames.add(entry.name);
    }
  }
  return packageNames;
}

function resolveManagedNpmRootPackageDir(npmRoot: string, packageName: string): string {
  return path.join(npmRoot, "node_modules", ...packageName.split("/"));
}

async function listNewManagedNpmRootPackageDirs(params: {
  beforeInstallPackageNames: Set<string>;
  npmRoot: string;
}): Promise<string[]> {
  const afterInstallPackageNames = await listManagedNpmRootPackageNames(params.npmRoot);
  return [...afterInstallPackageNames]
    .filter((packageName) => !params.beforeInstallPackageNames.has(packageName))
    .map((packageName) => resolveManagedNpmRootPackageDir(params.npmRoot, packageName))
    .toSorted((left, right) => left.localeCompare(right));
}

function resolveTrustedNpmPackPackageName(packageName: string | undefined):
  | {
      ok: true;
      packageName: string;
    }
  | {
      ok: false;
      error: string;
      code: PluginInstallErrorCode;
    } {
  if (!packageName) {
    return {
      ok: false,
      error: "npm pack metadata missing package name",
      code: PLUGIN_INSTALL_ERROR_CODE.INVALID_NPM_SPEC,
    };
  }
  const specError = validateRegistryNpmSpec(packageName);
  const parsedSpec = parseRegistryNpmSpec(packageName);
  if (specError || !parsedSpec || parsedSpec.selectorKind !== "none") {
    return {
      ok: false,
      error: `unsupported npm pack package name: ${packageName}`,
      code: PLUGIN_INSTALL_ERROR_CODE.INVALID_NPM_SPEC,
    };
  }
  return { ok: true, packageName: parsedSpec.name };
}

async function installPluginFromManagedNpmRoot(
  params: InstallSafetyOverrides & {
    packageName: string;
    dependencySpec: string;
    displaySpec: string;
    installPolicyRequest: PluginInstallPolicyRequest;
    npmResolution: NpmSpecResolution;
    extensionsDir?: string;
    npmDir?: string;
    timeoutMs?: number;
    logger?: PluginInstallLogger;
    mode?: "install" | "update";
    dryRun?: boolean;
    expectedPluginId?: string;
    integrityDrift?: NpmIntegrityDrift;
  },
): Promise<InstallPluginResult> {
  const runtime = await loadPluginInstallRuntime();
  const { logger, timeoutMs, mode, dryRun } = runtime.resolveTimedInstallModeOptions(
    params,
    defaultLogger,
  );
  const expectedPluginId = params.expectedPluginId;
  const npmBaseDir = params.npmDir ? resolveUserPath(params.npmDir) : resolveDefaultPluginNpmDir();
  const npmRoot = resolvePluginNpmProjectDir({
    npmDir: npmBaseDir,
    packageName: params.packageName,
  });
  const installRoot = resolveManagedNpmRootPackageDir(npmRoot, params.packageName);
  const effectiveMode = await resolveEffectiveInstallMode({
    runtime,
    requestedMode: mode,
    targetPath: installRoot,
  });
  const availability = await ensureInstallTargetAvailableForMode({
    runtime,
    targetPath: installRoot,
    mode: effectiveMode,
  });
  if (!availability.ok) {
    return availability;
  }
  if (dryRun) {
    return {
      ok: true,
      pluginId: expectedPluginId ?? params.packageName,
      targetDir: installRoot,
      extensions: [],
      npmResolution: params.npmResolution,
      ...(params.integrityDrift ? { integrityDrift: params.integrityDrift } : {}),
    };
  }

  logger.info?.(`Installing ${params.displaySpec} into ${npmRoot}…`);
  if (params.packageName !== "openclaw") {
    const repairedOpenClawPeer = await repairManagedNpmRootOpenClawPeer({
      npmRoot,
      timeoutMs,
      logger,
    });
    if (repairedOpenClawPeer) {
      logger.info?.(`Repaired stale openclaw peer dependency in ${npmRoot}`);
    }
  }
  const preInstallRootPackageNames = await listManagedNpmRootPackageNames(npmRoot);
  const managedOverrides = await readOpenClawManagedNpmRootOverrides();
  const rollbackPeerDependencySnapshot = await readManagedNpmRootPeerDependencySnapshot({
    npmRoot,
  });
  const rollbackFailedManagedNpmInstall = async (error: string): Promise<InstallPluginResult> => {
    await rollbackManagedNpmPluginInstall({
      npmRoot,
      packageName: params.packageName,
      targetDir: installRoot,
      timeoutMs,
      logger,
      peerDependencySnapshot: rollbackPeerDependencySnapshot,
    });
    return { ok: false, error };
  };
  const syncManagedPeerDependenciesForInstall = async (options?: {
    omitUnsupportedManagedOverrides?: boolean;
  }): Promise<{ ok: true; changed: boolean } | { ok: false; error: string }> => {
    try {
      return {
        ok: true,
        changed: await syncManagedNpmRootPeerDependencies({
          npmRoot,
          managedOverrides,
          omitUnsupportedManagedOverrides: options?.omitUnsupportedManagedOverrides,
          timeoutMs,
        }),
      };
    } catch (error) {
      return {
        ok: false,
        error: `npm peer dependency planning failed: ${error instanceof Error ? error.message : String(error)}`,
      };
    }
  };
  await upsertManagedNpmRootDependency({
    npmRoot,
    packageName: params.packageName,
    dependencySpec: params.dependencySpec,
    managedOverrides,
  });
  const initialPeerSync = await syncManagedPeerDependenciesForInstall();
  if (!initialPeerSync.ok) {
    return await rollbackFailedManagedNpmInstall(initialPeerSync.error);
  }
  const npmInstallArgs = [
    "npm",
    ...createSafeNpmInstallArgs({
      omitDev: true,
      omitPeer: true,
      loglevel: "error",
      legacyPeerDeps: true,
      noAudit: true,
      noFund: true,
    }),
  ];
  const npmInstallOptions = {
    cwd: npmRoot,
    timeoutMs: Math.max(timeoutMs, 300_000),
    env: createSafeNpmInstallEnv(process.env, {
      legacyPeerDeps: true,
      npmConfigCwd: npmRoot,
      packageLock: true,
      quiet: true,
    }),
  };
  let install = await runCommandWithTimeout(npmInstallArgs, npmInstallOptions);
  let omitUnsupportedManagedOverrides = false;
  if (install.code !== 0 && isNpmAliasOverrideComparatorError(install)) {
    logger.warn?.(
      "npm rejected managed npm alias overrides; retrying plugin install without alias overrides for this npm version.",
    );
    omitUnsupportedManagedOverrides = true;
    await upsertManagedNpmRootDependency({
      npmRoot,
      packageName: params.packageName,
      dependencySpec: params.dependencySpec,
      managedOverrides,
      omitUnsupportedManagedOverrides: true,
    });
    const aliasRetryPeerSync = await syncManagedPeerDependenciesForInstall({
      omitUnsupportedManagedOverrides: true,
    });
    if (!aliasRetryPeerSync.ok) {
      return await rollbackFailedManagedNpmInstall(aliasRetryPeerSync.error);
    }
    install = await runCommandWithTimeout(npmInstallArgs, npmInstallOptions);
  }
  if (install.code !== 0) {
    await rollbackManagedNpmPluginInstall({
      npmRoot,
      packageName: params.packageName,
      targetDir: installRoot,
      timeoutMs,
      logger,
      peerDependencySnapshot: rollbackPeerDependencySnapshot,
    });
    return {
      ok: false,
      error: `npm install failed: ${install.stderr.trim() || install.stdout.trim()}`,
    };
  }
  let settledManagedPeerDependencies = false;
  for (let peerSyncPass = 0; peerSyncPass < 10; peerSyncPass += 1) {
    const peerSync = await syncManagedPeerDependenciesForInstall({
      omitUnsupportedManagedOverrides,
    });
    if (!peerSync.ok) {
      return await rollbackFailedManagedNpmInstall(peerSync.error);
    }
    const syncedPeerDependencies = peerSync.changed;
    if (!syncedPeerDependencies) {
      settledManagedPeerDependencies = true;
      break;
    }
    install = await runCommandWithTimeout(npmInstallArgs, npmInstallOptions);
    if (install.code !== 0) {
      await rollbackManagedNpmPluginInstall({
        npmRoot,
        packageName: params.packageName,
        targetDir: installRoot,
        timeoutMs,
        logger,
        peerDependencySnapshot: rollbackPeerDependencySnapshot,
      });
      return {
        ok: false,
        error: `npm install failed after syncing managed peer dependencies: ${install.stderr.trim() || install.stdout.trim()}`,
      };
    }
  }
  if (!settledManagedPeerDependencies) {
    const peerSync = await syncManagedPeerDependenciesForInstall({
      omitUnsupportedManagedOverrides,
    });
    if (!peerSync.ok) {
      return await rollbackFailedManagedNpmInstall(peerSync.error);
    }
    settledManagedPeerDependencies = !peerSync.changed;
  }
  if (!settledManagedPeerDependencies) {
    await rollbackManagedNpmPluginInstall({
      npmRoot,
      packageName: params.packageName,
      targetDir: installRoot,
      timeoutMs,
      logger,
      peerDependencySnapshot: rollbackPeerDependencySnapshot,
    });
    return {
      ok: false,
      error:
        "npm install could not settle managed peer dependencies after 10 sync passes; refusing to leave a partially reconciled plugin dependency tree.",
    };
  }
  if (params.packageName !== "openclaw") {
    const repairedOpenClawPeer = await repairManagedNpmRootOpenClawPeer({
      npmRoot,
      timeoutMs,
      logger,
    });
    if (repairedOpenClawPeer) {
      logger.info?.(`Repaired stale openclaw peer dependency in ${npmRoot} after npm install`);
    }
  }
  try {
    await relinkOpenClawPeerDependenciesInManagedNpmRoot({
      npmRoot,
      logger,
    });
  } catch (error) {
    await rollbackManagedNpmPluginInstall({
      npmRoot,
      packageName: params.packageName,
      targetDir: installRoot,
      timeoutMs,
      logger,
      peerDependencySnapshot: rollbackPeerDependencySnapshot,
    });
    return {
      ok: false,
      error: `Failed to repair openclaw peer links after npm install: ${String(error)}`,
    };
  }
  if (installedPackageNeedsOpenClawPeerLinkRepair(installRoot)) {
    await rollbackManagedNpmPluginInstall({
      npmRoot,
      packageName: params.packageName,
      targetDir: installRoot,
      timeoutMs,
      logger,
      peerDependencySnapshot: rollbackPeerDependencySnapshot,
    });
    return {
      ok: false,
      error: formatUnresolvedOpenClawPeerLinkError(params.packageName),
    };
  }

  let installedDependency: ManagedNpmRootInstalledDependency | null;
  try {
    installedDependency = await readManagedNpmRootInstalledDependency({
      npmRoot,
      packageName: params.packageName,
    });
  } catch (error) {
    await rollbackManagedNpmPluginInstall({
      npmRoot,
      packageName: params.packageName,
      targetDir: installRoot,
      timeoutMs,
      logger,
      peerDependencySnapshot: rollbackPeerDependencySnapshot,
    });
    return {
      ok: false,
      error: `Failed to verify npm install metadata for ${params.packageName}: ${String(error)}`,
    };
  }
  const resolutionMismatch = resolveInstalledNpmResolutionMismatch({
    packageName: params.packageName,
    expected: params.npmResolution,
    installed: installedDependency,
  });
  if (resolutionMismatch) {
    await rollbackManagedNpmPluginInstall({
      npmRoot,
      packageName: params.packageName,
      targetDir: installRoot,
      timeoutMs,
      logger,
      peerDependencySnapshot: rollbackPeerDependencySnapshot,
    });
    return {
      ok: false,
      error: resolutionMismatch,
    };
  }

  const newRootPackageDirs = await listNewManagedNpmRootPackageDirs({
    beforeInstallPackageNames: preInstallRootPackageNames,
    npmRoot,
  });
  const result = await installPluginFromInstalledPackageDir({
    dangerouslyForceUnsafeInstall: params.dangerouslyForceUnsafeInstall,
    additionalDependencyPackageDirs: newRootPackageDirs,
    packageDir: installRoot,
    dependencyScanRootDir: npmRoot,
    logger,
    expectedPluginId,
    trustedSourceLinkedOfficialInstall: params.trustedSourceLinkedOfficialInstall,
    mode: effectiveMode,
    installPolicyRequest: params.installPolicyRequest,
  });
  if (!result.ok) {
    await rollbackManagedNpmPluginInstall({
      npmRoot,
      packageName: params.packageName,
      targetDir: installRoot,
      timeoutMs,
      logger,
      peerDependencySnapshot: rollbackPeerDependencySnapshot,
    });
    return result;
  }
  return {
    ...result,
    npmResolution: params.npmResolution,
    ...(params.integrityDrift ? { integrityDrift: params.integrityDrift } : {}),
  };
}

async function stageNpmPackArchiveInManagedRoot(params: {
  archivePath: string;
  npmRoot: string;
  packageName: string;
  version?: string;
  integrity?: string;
  shasum?: string;
  tarballName: string;
}): Promise<{ stableArchivePath: string; dependencySpec: string }> {
  const archiveStoreDir = path.join(params.npmRoot, MANAGED_NPM_PACK_ARCHIVE_DIR);
  const identity = params.integrity ?? params.shasum ?? params.tarballName;
  const identitySlug = createHash("sha256").update(identity).digest("hex").slice(0, 16);
  const packageSlug = safePluginInstallFileName(params.packageName) || "plugin";
  const versionSlug = safePluginInstallFileName(params.version ?? "pack") || "pack";
  const archiveFileName = `${packageSlug}-${versionSlug}-${identitySlug}.tgz`;
  const stableArchivePath = path.join(archiveStoreDir, archiveFileName);

  await fs.mkdir(archiveStoreDir, { recursive: true });
  await fs.copyFile(params.archivePath, stableArchivePath);

  return {
    stableArchivePath,
    dependencySpec: `file:./${path.posix.join(MANAGED_NPM_PACK_ARCHIVE_DIR, archiveFileName)}`,
  };
}

type PackageInstallCommonParams = InstallSafetyOverrides & {
  extensionsDir?: string;
  npmDir?: string;
  timeoutMs?: number;
  logger?: PluginInstallLogger;
  mode?: "install" | "update";
  dryRun?: boolean;
  expectedPluginId?: string;
  requirePluginManifest?: boolean;
  allowSourceTypeScriptEntries?: boolean;
  installPolicyRequest?: PluginInstallPolicyRequest;
};

type FileInstallCommonParams = Pick<
  PackageInstallCommonParams,
  | "dangerouslyForceUnsafeInstall"
  | "trustedSourceLinkedOfficialInstall"
  | "extensionsDir"
  | "logger"
  | "mode"
  | "dryRun"
  | "installPolicyRequest"
>;

function pickPackageInstallCommonParams(
  params: PackageInstallCommonParams,
): PackageInstallCommonParams {
  return {
    dangerouslyForceUnsafeInstall: params.dangerouslyForceUnsafeInstall,
    trustedSourceLinkedOfficialInstall: params.trustedSourceLinkedOfficialInstall,
    extensionsDir: params.extensionsDir,
    npmDir: params.npmDir,
    timeoutMs: params.timeoutMs,
    logger: params.logger,
    mode: params.mode,
    dryRun: params.dryRun,
    expectedPluginId: params.expectedPluginId,
    requirePluginManifest: params.requirePluginManifest,
    allowSourceTypeScriptEntries: params.allowSourceTypeScriptEntries,
    installPolicyRequest: params.installPolicyRequest,
  };
}

function pickFileInstallCommonParams(params: FileInstallCommonParams): FileInstallCommonParams {
  return {
    dangerouslyForceUnsafeInstall: params.dangerouslyForceUnsafeInstall,
    extensionsDir: params.extensionsDir,
    logger: params.logger,
    mode: params.mode,
    dryRun: params.dryRun,
    installPolicyRequest: params.installPolicyRequest,
  };
}

type PreparedInstallTarget = {
  targetPath: string;
  effectiveMode: "install" | "update";
};

async function ensureInstallTargetAvailableForMode(params: {
  runtime: Awaited<ReturnType<typeof loadPluginInstallRuntime>>;
  targetPath: string;
  mode: "install" | "update";
}): Promise<{ ok: true } | { ok: false; error: string }> {
  return await params.runtime.ensureInstallTargetAvailable({
    mode: params.mode,
    targetDir: params.targetPath,
    alreadyExistsError: `plugin already exists: ${params.targetPath} (delete it first)`,
  });
}

async function resolvePreparedDirectoryInstallTarget(params: {
  runtime: Awaited<ReturnType<typeof loadPluginInstallRuntime>>;
  pluginId: string;
  extensionsDir?: string;
  requestedMode: "install" | "update";
  nameEncoder?: (pluginId: string) => string;
}): Promise<{ ok: true; target: PreparedInstallTarget } | { ok: false; error: string }> {
  const targetDirResult = await resolvePluginInstallTarget({
    runtime: params.runtime,
    pluginId: params.pluginId,
    extensionsDir: params.extensionsDir,
    nameEncoder: params.nameEncoder,
  });
  if (!targetDirResult.ok) {
    return targetDirResult;
  }
  return {
    ok: true,
    target: {
      targetPath: targetDirResult.targetDir,
      effectiveMode: await resolveEffectiveInstallMode({
        runtime: params.runtime,
        requestedMode: params.requestedMode,
        targetPath: targetDirResult.targetDir,
      }),
    },
  };
}

async function runInstallSourceScan(params: {
  subject: string;
  scan: () => Promise<InstallSecurityScanResult | undefined>;
}): Promise<Extract<InstallPluginResult, { ok: false }> | null> {
  try {
    const scanResult = await params.scan();
    if (scanResult?.blocked) {
      return buildBlockedInstallResult({ blocked: scanResult.blocked });
    }
    return null;
  } catch (err) {
    return {
      ok: false,
      error: `${params.subject} installation blocked: code safety scan failed (${String(err)}). Run "openclaw security audit --deep" for details.`,
      code: PLUGIN_INSTALL_ERROR_CODE.SECURITY_SCAN_FAILED,
    };
  }
}

async function installPluginDirectoryIntoExtensions(params: {
  sourceDir: string;
  pluginId: string;
  manifestName?: string;
  version?: string;
  extensions: string[];
  targetDir?: string;
  extensionsDir?: string;
  logger: PluginInstallLogger;
  timeoutMs: number;
  mode: "install" | "update";
  dryRun: boolean;
  copyErrorPrefix: string;
  hasDeps: boolean;
  sourceHardlinks?: "package-manager" | "reject";
  depsLogMessage: string;
  afterCopy?: (installedDir: string) => Promise<void>;
  afterInstall?: (
    installedDir: string,
  ) => Promise<Extract<InstallPluginResult, { ok: false }> | null>;
  nameEncoder?: (pluginId: string) => string;
}): Promise<InstallPluginResult> {
  const runtime = await loadPluginInstallRuntime();
  let targetDir = params.targetDir;
  if (!targetDir) {
    const targetDirResult = await resolvePluginInstallTarget({
      runtime,
      pluginId: params.pluginId,
      extensionsDir: params.extensionsDir,
      nameEncoder: params.nameEncoder,
    });
    if (!targetDirResult.ok) {
      return { ok: false, error: targetDirResult.error };
    }
    targetDir = targetDirResult.targetDir;
  }
  const availability = await ensureInstallTargetAvailableForMode({
    runtime,
    targetPath: targetDir,
    mode: params.mode,
  });
  if (!availability.ok) {
    return availability;
  }

  if (params.dryRun) {
    return buildDirectoryInstallResult({
      pluginId: params.pluginId,
      targetDir,
      manifestName: params.manifestName,
      version: params.version,
      extensions: params.extensions,
    });
  }

  const installRes = await runtime.installPackageDir({
    sourceDir: params.sourceDir,
    targetDir,
    mode: params.mode,
    timeoutMs: params.timeoutMs,
    logger: params.logger,
    copyErrorPrefix: params.copyErrorPrefix,
    hasDeps: params.hasDeps,
    sourceHardlinks: params.sourceHardlinks ?? "reject",
    depsLogMessage: params.depsLogMessage,
    afterCopy: params.afterCopy,
    afterInstall: async (installedDir) => {
      const postInstallResult = await params.afterInstall?.(installedDir);
      if (!postInstallResult) {
        return { ok: true as const };
      }
      return {
        ok: false as const,
        error: postInstallResult.error,
        ...(postInstallResult.code ? { code: postInstallResult.code } : {}),
      };
    },
  });
  if (!installRes.ok) {
    return {
      ok: false,
      error: installRes.error,
      ...(installRes.code ? { code: installRes.code as PluginInstallErrorCode } : {}),
    };
  }

  return buildDirectoryInstallResult({
    pluginId: params.pluginId,
    targetDir,
    manifestName: params.manifestName,
    version: params.version,
    extensions: params.extensions,
  });
}

async function resolvePluginInstallTarget(params: {
  runtime: Awaited<ReturnType<typeof loadPluginInstallRuntime>>;
  pluginId: string;
  extensionsDir?: string;
  nameEncoder?: (pluginId: string) => string;
}): Promise<{ ok: true; targetDir: string } | { ok: false; error: string }> {
  const extensionsDir = params.extensionsDir
    ? resolveUserPath(params.extensionsDir)
    : resolveDefaultPluginExtensionsDir();
  return await params.runtime.resolveCanonicalInstallTarget({
    baseDir: extensionsDir,
    id: params.pluginId,
    invalidNameMessage: "invalid plugin name: path traversal detected",
    boundaryLabel: "extensions directory",
    nameEncoder: params.nameEncoder,
  });
}

async function resolveEffectiveInstallMode(params: {
  runtime: Awaited<ReturnType<typeof loadPluginInstallRuntime>>;
  requestedMode: "install" | "update";
  targetPath: string;
}): Promise<"install" | "update"> {
  if (params.requestedMode !== "update") {
    return "install";
  }
  return (await params.runtime.fileExists(params.targetPath)) ? "update" : "install";
}

async function installBundleFromSourceDir(
  params: {
    sourceDir: string;
  } & PackageInstallCommonParams,
): Promise<InstallPluginResult | null> {
  const runtime = await loadPluginInstallRuntime();
  const bundleFormat = runtime.detectBundleManifestFormat(params.sourceDir);
  if (!bundleFormat) {
    return null;
  }

  const { logger, timeoutMs, mode, dryRun } = runtime.resolveTimedInstallModeOptions(
    params,
    defaultLogger,
  );
  const manifestRes = runtime.loadBundleManifest({
    rootDir: params.sourceDir,
    bundleFormat,
    rejectHardlinks: true,
  });
  if (!manifestRes.ok) {
    return { ok: false, error: manifestRes.error };
  }

  const pluginId = manifestRes.manifest.id;
  const pluginIdError = validatePluginId(pluginId);
  if (pluginIdError) {
    return { ok: false, error: pluginIdError };
  }
  if (params.expectedPluginId && params.expectedPluginId !== pluginId) {
    return {
      ok: false,
      error: `plugin id mismatch: expected ${params.expectedPluginId}, got ${pluginId}`,
      code: PLUGIN_INSTALL_ERROR_CODE.PLUGIN_ID_MISMATCH,
    };
  }
  const packageManifestResult = await readOptionalPackageManifest({
    runtime,
    packageDir: params.sourceDir,
  });
  if (!packageManifestResult.ok) {
    return packageManifestResult;
  }
  const packageMetadata = packageManifestResult.manifest
    ? runtime.getPackageManifestMetadata(packageManifestResult.manifest)
    : undefined;
  const compatibilityError = validateOpenClawPackageInstallCompatibility({
    runtime,
    pluginId,
    packageMetadata,
  });
  if (compatibilityError) {
    return compatibilityError;
  }

  const targetResult = await resolvePreparedDirectoryInstallTarget({
    runtime,
    pluginId,
    extensionsDir: params.extensionsDir,
    requestedMode: mode,
  });
  if (!targetResult.ok) {
    return { ok: false, error: targetResult.error };
  }

  const scanResult = await runInstallSourceScan({
    subject: `Bundle "${pluginId}"`,
    scan: async () =>
      await runtime.scanBundleInstallSource({
        dangerouslyForceUnsafeInstall: params.dangerouslyForceUnsafeInstall,
        sourceDir: params.sourceDir,
        pluginId,
        logger,
        requestKind: params.installPolicyRequest?.kind,
        requestedSpecifier: params.installPolicyRequest?.requestedSpecifier,
        mode: targetResult.target.effectiveMode,
        version: manifestRes.manifest.version,
      }),
  });
  if (scanResult) {
    return scanResult;
  }

  return await installPluginDirectoryIntoExtensions({
    sourceDir: params.sourceDir,
    pluginId,
    manifestName: manifestRes.manifest.name,
    version: manifestRes.manifest.version,
    extensions: [],
    targetDir: targetResult.target.targetPath,
    extensionsDir: params.extensionsDir,
    logger,
    timeoutMs,
    mode: targetResult.target.effectiveMode,
    dryRun,
    copyErrorPrefix: "failed to copy plugin bundle",
    hasDeps: false,
    depsLogMessage: "",
  });
}

async function installPluginFromSourceDir(
  params: {
    sourceDir: string;
  } & PackageInstallCommonParams,
): Promise<InstallPluginResult> {
  const nativePackageDetected = await detectNativePackageInstallSource(params.sourceDir);
  if (nativePackageDetected) {
    return await installPluginFromPackageDir({
      packageDir: params.sourceDir,
      ...pickPackageInstallCommonParams(params),
    });
  }
  const bundleResult = await installBundleFromSourceDir({
    sourceDir: params.sourceDir,
    ...pickPackageInstallCommonParams(params),
  });
  if (bundleResult) {
    return bundleResult;
  }
  return await installPluginFromPackageDir({
    packageDir: params.sourceDir,
    ...pickPackageInstallCommonParams(params),
  });
}

async function detectNativePackageInstallSource(packageDir: string): Promise<boolean> {
  const runtime = await loadPluginInstallRuntime();
  const manifestPath = path.join(packageDir, "package.json");
  if (!(await runtime.fileExists(manifestPath))) {
    return false;
  }

  try {
    const manifest = await runtime.readJsonFile<PackageManifest>(manifestPath);
    return ensureOpenClawExtensions({ manifest }).ok;
  } catch {
    return false;
  }
}

type ValidatedPackagePlugin = {
  manifest: PackageManifest;
  pluginId: string;
  manifestName?: string;
  version?: string;
  extensions: string[];
  hasRuntimeDependencies: boolean;
  peerDependencies: Record<string, string>;
};

async function validatePackagePluginInstallSource(params: {
  runtime: Awaited<ReturnType<typeof loadPluginInstallRuntime>>;
  packageDir: string;
  expectedPluginId?: string;
  requirePluginManifest?: boolean;
  allowSourceTypeScriptEntries?: boolean;
  dangerouslyForceUnsafeInstall?: boolean;
  trustedSourceLinkedOfficialInstall?: boolean;
  installPolicyRequest?: PluginInstallPolicyRequest;
  logger: PluginInstallLogger;
  mode: "install" | "update";
  resolveEffectiveMode?: (pluginId: string) => Promise<"install" | "update">;
}): Promise<
  | {
      ok: true;
      plugin: ValidatedPackagePlugin;
    }
  | PluginInstallFailureResult
> {
  const manifestPath = path.join(params.packageDir, "package.json");
  if (!(await params.runtime.fileExists(manifestPath))) {
    return { ok: false, error: "extracted package missing package.json" };
  }

  let manifest: PackageManifest;
  try {
    manifest = await params.runtime.readJsonFile<PackageManifest>(manifestPath);
  } catch (err) {
    return { ok: false, error: `invalid package.json: ${String(err)}` };
  }

  const pkgName = normalizeOptionalString(manifest.name) ?? "";
  const npmPluginId = pkgName || "plugin";
  const ocManifestResult = params.runtime.loadPluginManifest(params.packageDir);
  if (!ocManifestResult.ok && params.requirePluginManifest) {
    return {
      ok: false,
      error: `package missing valid openclaw.plugin.json: ${ocManifestResult.error}`,
      code: PLUGIN_INSTALL_ERROR_CODE.MISSING_PLUGIN_MANIFEST,
    };
  }
  const manifestPluginId =
    ocManifestResult.ok && ocManifestResult.manifest.id
      ? ocManifestResult.manifest.id.trim()
      : undefined;

  const pluginId = manifestPluginId ?? npmPluginId;
  const pluginIdError = validatePluginId(pluginId);
  if (pluginIdError) {
    return { ok: false, error: pluginIdError };
  }
  if (
    !matchesExpectedPluginId({
      expectedPluginId: params.expectedPluginId,
      pluginId,
      manifestPluginId,
      npmPluginId,
    })
  ) {
    return {
      ok: false,
      error: `plugin id mismatch: expected ${params.expectedPluginId}, got ${pluginId}`,
      code: PLUGIN_INSTALL_ERROR_CODE.PLUGIN_ID_MISMATCH,
    };
  }

  if (manifestPluginId && !packageNameMatchesId(npmPluginId, manifestPluginId)) {
    params.logger.info?.(
      `Plugin manifest id "${manifestPluginId}" differs from npm package name "${npmPluginId}"; using manifest id as the config key.`,
    );
  }

  const packageMetadata = params.runtime.getPackageManifestMetadata(manifest);
  const compatibilityError = validateOpenClawPackageInstallCompatibility({
    runtime: params.runtime,
    pluginId,
    packageMetadata,
  });
  if (compatibilityError) {
    return compatibilityError;
  }

  const extensionsResult = ensureOpenClawExtensions({
    manifest,
  });
  if (!extensionsResult.ok) {
    return {
      ok: false,
      error: extensionsResult.error,
      code: extensionsResult.code,
    };
  }
  const extensions = extensionsResult.entries;

  const extensionValidation = await validatePackageExtensionEntriesForInstall({
    packageDir: params.packageDir,
    extensions,
    manifest,
    allowSourceTypeScriptEntries: params.allowSourceTypeScriptEntries,
  });
  if (!extensionValidation.ok) {
    return {
      ok: false,
      error: extensionValidation.error,
      code: PLUGIN_INSTALL_ERROR_CODE.INVALID_OPENCLAW_EXTENSIONS,
    };
  }

  const scanMode = params.resolveEffectiveMode
    ? await params.resolveEffectiveMode(pluginId)
    : params.mode;
  const scanResult = await runInstallSourceScan({
    subject: `Plugin "${pluginId}"`,
    scan: async () =>
      await params.runtime.scanPackageInstallSource({
        dangerouslyForceUnsafeInstall: params.dangerouslyForceUnsafeInstall,
        trustedSourceLinkedOfficialInstall: params.trustedSourceLinkedOfficialInstall,
        packageDir: params.packageDir,
        pluginId,
        logger: params.logger,
        extensions,
        ...(packageMetadata ? { packageMetadata } : {}),
        requestKind: params.installPolicyRequest?.kind,
        requestedSpecifier: params.installPolicyRequest?.requestedSpecifier,
        mode: scanMode,
        packageName: pkgName || undefined,
        manifestId: manifestPluginId,
        version: typeof manifest.version === "string" ? manifest.version : undefined,
      }),
  });
  if (scanResult) {
    return scanResult;
  }

  return {
    ok: true,
    plugin: {
      manifest,
      pluginId,
      manifestName: pkgName || undefined,
      version: typeof manifest.version === "string" ? manifest.version : undefined,
      extensions,
      hasRuntimeDependencies: hasPackageRuntimeDependencies(manifest),
      peerDependencies: manifest.peerDependencies ?? {},
    },
  };
}

async function scanAndLinkInstalledPackage(params: {
  runtime: Awaited<ReturnType<typeof loadPluginInstallRuntime>>;
  installedDir: string;
  additionalDependencyPackageDirs?: string[];
  dependencyScanRootDir?: string;
  pluginId: string;
  peerDependencies: Record<string, string>;
  dangerouslyForceUnsafeInstall?: boolean;
  trustedSourceLinkedOfficialInstall?: boolean;
  logger: PluginInstallLogger;
}): Promise<Extract<InstallPluginResult, { ok: false }> | null> {
  const scanResult = await runInstallSourceScan({
    subject: `Plugin "${params.pluginId}"`,
    scan: async () =>
      await params.runtime.scanInstalledPackageDependencyTree({
        ...(params.additionalDependencyPackageDirs
          ? { additionalPackageDirs: params.additionalDependencyPackageDirs }
          : {}),
        allowManagedNpmRootPackagePeerSymlinks:
          params.dependencyScanRootDir !== undefined &&
          path.resolve(params.dependencyScanRootDir) !== path.resolve(params.installedDir),
        dangerouslyForceUnsafeInstall: params.dangerouslyForceUnsafeInstall,
        dependencyScanRootDir: params.dependencyScanRootDir,
        logger: params.logger,
        packageDir: params.installedDir,
        pluginId: params.pluginId,
        trustedSourceLinkedOfficialInstall: params.trustedSourceLinkedOfficialInstall,
      }),
  });
  if (scanResult) {
    return scanResult;
  }
  const peerLinkRepair = await linkOpenClawPeerDependencies({
    installedDir: params.installedDir,
    peerDependencies: params.peerDependencies,
    logger: params.logger,
  });
  if (peerLinkRepair.skipped > 0) {
    return {
      ok: false,
      error: formatUnresolvedOpenClawPeerLinkError(params.pluginId),
    };
  }
  return null;
}

export async function installPluginFromInstalledPackageDir(
  params: {
    additionalDependencyPackageDirs?: string[];
    packageDir: string;
    dependencyScanRootDir?: string;
  } & PackageInstallCommonParams,
): Promise<InstallPluginResult> {
  const runtime = await loadPluginInstallRuntime();
  const { logger } = runtime.resolveTimedInstallModeOptions(params, defaultLogger);
  const validated = await validatePackagePluginInstallSource({
    runtime,
    packageDir: params.packageDir,
    expectedPluginId: params.expectedPluginId,
    requirePluginManifest: params.requirePluginManifest,
    allowSourceTypeScriptEntries: params.allowSourceTypeScriptEntries,
    dangerouslyForceUnsafeInstall: params.dangerouslyForceUnsafeInstall,
    trustedSourceLinkedOfficialInstall: params.trustedSourceLinkedOfficialInstall,
    installPolicyRequest: params.installPolicyRequest,
    logger,
    mode: params.mode ?? "install",
  });
  if (!validated.ok) {
    return validated;
  }
  const postInstallError = await scanAndLinkInstalledPackage({
    runtime,
    installedDir: params.packageDir,
    ...(params.additionalDependencyPackageDirs
      ? { additionalDependencyPackageDirs: params.additionalDependencyPackageDirs }
      : {}),
    dependencyScanRootDir: params.dependencyScanRootDir,
    pluginId: validated.plugin.pluginId,
    peerDependencies: validated.plugin.peerDependencies,
    dangerouslyForceUnsafeInstall: params.dangerouslyForceUnsafeInstall,
    trustedSourceLinkedOfficialInstall: params.trustedSourceLinkedOfficialInstall,
    logger,
  });
  if (postInstallError) {
    return postInstallError;
  }
  return buildDirectoryInstallResult({
    pluginId: validated.plugin.pluginId,
    targetDir: params.packageDir,
    manifestName: validated.plugin.manifestName,
    version: validated.plugin.version,
    extensions: validated.plugin.extensions,
  });
}

async function installPluginFromPackageDir(
  params: {
    packageDir: string;
  } & PackageInstallCommonParams,
): Promise<InstallPluginResult> {
  const runtime = await loadPluginInstallRuntime();
  const { logger, timeoutMs, mode, dryRun } = runtime.resolveTimedInstallModeOptions(
    params,
    defaultLogger,
  );
  let preparedTarget: PreparedInstallTarget | undefined;
  const resolvePreparedTargetForPluginId = async (pluginId: string) => {
    if (!preparedTarget) {
      const targetResult = await resolvePreparedDirectoryInstallTarget({
        runtime,
        pluginId,
        extensionsDir: params.extensionsDir,
        requestedMode: mode,
        nameEncoder: encodePluginInstallDirName,
      });
      if (!targetResult.ok) {
        throw new Error(targetResult.error);
      }
      preparedTarget = targetResult.target;
    }
    return preparedTarget;
  };

  const validated = await validatePackagePluginInstallSource({
    runtime,
    packageDir: params.packageDir,
    expectedPluginId: params.expectedPluginId,
    requirePluginManifest: params.requirePluginManifest,
    allowSourceTypeScriptEntries: params.allowSourceTypeScriptEntries,
    dangerouslyForceUnsafeInstall: params.dangerouslyForceUnsafeInstall,
    trustedSourceLinkedOfficialInstall: params.trustedSourceLinkedOfficialInstall,
    installPolicyRequest: params.installPolicyRequest,
    logger,
    mode,
    resolveEffectiveMode: async (pluginId) =>
      (await resolvePreparedTargetForPluginId(pluginId)).effectiveMode,
  });
  if (!validated.ok) {
    return validated;
  }
  const { plugin } = validated;

  preparedTarget = await resolvePreparedTargetForPluginId(plugin.pluginId);
  const hasBundleManifest = Boolean(runtime.detectBundleManifestFormat(params.packageDir));
  const shouldInstallRuntimeDeps =
    plugin.hasRuntimeDependencies &&
    !hasBundleManifest &&
    params.installPolicyRequest?.kind === "plugin-archive";

  return await installPluginDirectoryIntoExtensions({
    sourceDir: params.packageDir,
    pluginId: plugin.pluginId,
    manifestName: plugin.manifestName,
    version: plugin.version,
    extensions: plugin.extensions,
    targetDir: preparedTarget.targetPath,
    extensionsDir: params.extensionsDir,
    logger,
    timeoutMs,
    mode: preparedTarget.effectiveMode,
    dryRun,
    copyErrorPrefix: "failed to copy plugin",
    hasDeps: shouldInstallRuntimeDeps,
    sourceHardlinks: shouldInstallRuntimeDeps ? "package-manager" : "reject",
    depsLogMessage: "Installing plugin dependencies…",
    nameEncoder: encodePluginInstallDirName,
    afterInstall: async (installedDir) => {
      return await scanAndLinkInstalledPackage({
        runtime,
        installedDir,
        pluginId: plugin.pluginId,
        peerDependencies: plugin.peerDependencies,
        dangerouslyForceUnsafeInstall: params.dangerouslyForceUnsafeInstall,
        trustedSourceLinkedOfficialInstall: params.trustedSourceLinkedOfficialInstall,
        logger,
      });
    },
  });
}

export async function installPluginFromArchive(
  params: {
    archivePath: string;
  } & PackageInstallCommonParams,
): Promise<InstallPluginResult> {
  const runtime = await loadPluginInstallRuntime();
  const logger = params.logger ?? defaultLogger;
  const timeoutMs = params.timeoutMs ?? 120_000;
  const mode = params.mode ?? "install";
  const installPolicyRequest = params.installPolicyRequest ?? {
    kind: "plugin-archive",
    requestedSpecifier: params.archivePath,
  };
  const archivePathResult = await runtime.resolveArchiveSourcePath(params.archivePath);
  if (!archivePathResult.ok) {
    return archivePathResult;
  }
  const archivePath = archivePathResult.path;

  return await runtime.withExtractedArchiveRoot({
    archivePath,
    tempDirPrefix: "openclaw-plugin-",
    timeoutMs,
    logger,
    rootMarkers: PLUGIN_ARCHIVE_ROOT_MARKERS,
    onExtracted: async (sourceDir) =>
      await installPluginFromSourceDir({
        sourceDir,
        ...pickPackageInstallCommonParams({
          dangerouslyForceUnsafeInstall: params.dangerouslyForceUnsafeInstall,
          extensionsDir: params.extensionsDir,
          timeoutMs,
          logger,
          mode,
          dryRun: params.dryRun,
          expectedPluginId: params.expectedPluginId,
          trustedSourceLinkedOfficialInstall: params.trustedSourceLinkedOfficialInstall,
          requirePluginManifest: true,
          installPolicyRequest,
        }),
      }),
  });
}

export async function installPluginFromDir(
  params: {
    dirPath: string;
  } & PackageInstallCommonParams,
): Promise<InstallPluginResult> {
  const runtime = await loadPluginInstallRuntime();
  const dirPath = resolveUserPath(params.dirPath);
  const installPolicyRequest = params.installPolicyRequest ?? {
    kind: "plugin-dir",
    requestedSpecifier: params.dirPath,
  };
  if (!(await runtime.fileExists(dirPath))) {
    return { ok: false, error: `directory not found: ${dirPath}` };
  }
  const stat = await fs.stat(dirPath);
  if (!stat.isDirectory()) {
    return { ok: false, error: `not a directory: ${dirPath}` };
  }

  return await installPluginFromSourceDir({
    sourceDir: dirPath,
    ...pickPackageInstallCommonParams({
      ...params,
      installPolicyRequest,
    }),
  });
}

export async function installPluginFromFile(params: {
  filePath: string;
  dangerouslyForceUnsafeInstall?: boolean;
  extensionsDir?: string;
  logger?: PluginInstallLogger;
  mode?: "install" | "update";
  dryRun?: boolean;
  installPolicyRequest?: PluginInstallPolicyRequest;
}): Promise<InstallPluginResult> {
  const runtime = await loadPluginInstallRuntime();
  const { logger, mode, dryRun } = runtime.resolveInstallModeOptions(params, defaultLogger);

  const filePath = resolveUserPath(params.filePath);
  const installPolicyRequest = params.installPolicyRequest ?? {
    kind: "plugin-file",
    requestedSpecifier: params.filePath,
  };
  if (!(await runtime.fileExists(filePath))) {
    return { ok: false, error: `file not found: ${filePath}` };
  }

  const extensionsDir = params.extensionsDir
    ? resolveUserPath(params.extensionsDir)
    : resolveDefaultPluginExtensionsDir();
  await fs.mkdir(extensionsDir, { recursive: true });

  const base = path.basename(filePath, path.extname(filePath));
  const pluginId = base || "plugin";
  const pluginIdError = validatePluginId(pluginId);
  if (pluginIdError) {
    return { ok: false, error: pluginIdError };
  }
  const targetFile = path.join(
    extensionsDir,
    `${safePluginInstallFileName(pluginId)}${path.extname(filePath)}`,
  );
  const preparedTarget: PreparedInstallTarget = {
    targetPath: targetFile,
    effectiveMode: await resolveEffectiveInstallMode({
      runtime,
      requestedMode: mode,
      targetPath: targetFile,
    }),
  };

  const availability = await ensureInstallTargetAvailableForMode({
    runtime,
    targetPath: preparedTarget.targetPath,
    mode: preparedTarget.effectiveMode,
  });
  if (!availability.ok) {
    return availability;
  }

  if (dryRun) {
    return buildFileInstallResult(pluginId, preparedTarget.targetPath);
  }

  const scanResult = await runInstallSourceScan({
    subject: `Plugin file "${pluginId}"`,
    scan: async () =>
      await runtime.scanFileInstallSource({
        dangerouslyForceUnsafeInstall: params.dangerouslyForceUnsafeInstall,
        filePath,
        logger,
        mode: preparedTarget.effectiveMode,
        pluginId,
        requestedSpecifier: installPolicyRequest.requestedSpecifier,
      }),
  });
  if (scanResult) {
    return scanResult;
  }

  logger.info?.(`Installing to ${preparedTarget.targetPath}…`);
  try {
    const root = await runtime.root(extensionsDir);
    await root.copyIn(path.basename(preparedTarget.targetPath), filePath);
  } catch (err) {
    return { ok: false, error: String(err) };
  }

  return buildFileInstallResult(pluginId, preparedTarget.targetPath);
}

export async function installPluginFromNpmSpec(
  params: InstallSafetyOverrides & {
    spec: string;
    extensionsDir?: string;
    npmDir?: string;
    timeoutMs?: number;
    logger?: PluginInstallLogger;
    mode?: "install" | "update";
    dryRun?: boolean;
    expectedPluginId?: string;
    expectedIntegrity?: string;
    onIntegrityDrift?: (params: PluginNpmIntegrityDriftParams) => boolean | Promise<boolean>;
  },
): Promise<InstallPluginResult> {
  const runtime = await loadPluginInstallRuntime();
  const { logger, timeoutMs, mode, dryRun } = runtime.resolveTimedInstallModeOptions(
    params,
    defaultLogger,
  );
  const expectedPluginId = params.expectedPluginId;
  const spec = params.spec.trim();
  const specError = runtime.validateRegistryNpmSpec(spec);
  if (specError) {
    return {
      ok: false,
      error: specError,
      code: PLUGIN_INSTALL_ERROR_CODE.INVALID_NPM_SPEC,
    };
  }

  const parsedSpec = parseRegistryNpmSpec(spec);
  if (!parsedSpec) {
    return {
      ok: false,
      error: "unsupported npm spec",
      code: PLUGIN_INSTALL_ERROR_CODE.INVALID_NPM_SPEC,
    };
  }

  const metadataResult = await resolveNpmSpecMetadata({ spec, timeoutMs });
  if (!metadataResult.ok) {
    return {
      ok: false,
      error: metadataResult.error,
      ...(isNpmPackageNotFoundMessage(metadataResult.error)
        ? { code: PLUGIN_INSTALL_ERROR_CODE.NPM_PACKAGE_NOT_FOUND }
        : {}),
    };
  }
  const npmResolution: NpmSpecResolution = {
    ...metadataResult.metadata,
    resolvedAt: new Date().toISOString(),
  };
  if (
    npmResolution.version &&
    !isPrereleaseResolutionAllowed({
      spec: parsedSpec,
      resolvedVersion: npmResolution.version,
    })
  ) {
    const trustedResolution = params.trustedSourceLinkedOfficialInstall
      ? await resolveTrustedOfficialPrereleaseResolution({
          spec: parsedSpec,
          resolvedPrereleaseVersion: npmResolution.version,
          timeoutMs,
          logger,
        })
      : null;
    if (trustedResolution?.kind === "stable" || trustedResolution?.kind === "prerelease-only") {
      Object.assign(npmResolution, trustedResolution.resolution, {
        resolvedAt: npmResolution.resolvedAt,
      });
    } else if (trustedResolution?.kind === "allow-prerelease-only") {
      // Keep the original prerelease resolution. The package has no stable line yet.
    } else {
      return {
        ok: false,
        error: formatPrereleaseResolutionError({
          spec: parsedSpec,
          resolvedVersion: npmResolution.version,
        }),
      };
    }
  }
  let compatibilityError = validateNpmResolutionCompatibility({
    runtime,
    parsedSpec,
    expectedPluginId,
    resolution: npmResolution,
  });
  if (compatibilityError && canResolveAroundCompatibilityError(compatibilityError)) {
    const compatibleResolution = await resolveLatestCompatibleNpmResolution({
      runtime,
      parsedSpec,
      expectedPluginId,
      currentResolution: npmResolution,
      timeoutMs,
      logger,
    });
    if (compatibleResolution) {
      Object.assign(npmResolution, compatibleResolution, {
        resolvedAt: npmResolution.resolvedAt,
      });
      compatibilityError = validateNpmResolutionCompatibility({
        runtime,
        parsedSpec,
        expectedPluginId,
        resolution: npmResolution,
      });
    }
  }
  if (compatibilityError) {
    return compatibilityError;
  }
  const driftResult = await resolveNpmIntegrityDriftWithDefaultMessage({
    spec,
    expectedIntegrity: params.expectedIntegrity,
    resolution: npmResolution,
    onIntegrityDrift: params.onIntegrityDrift,
    warn: (message) => logger.warn?.(message),
  });
  if (driftResult.error) {
    return { ok: false, error: driftResult.error };
  }

  return await installPluginFromManagedNpmRoot({
    dangerouslyForceUnsafeInstall: params.dangerouslyForceUnsafeInstall,
    trustedSourceLinkedOfficialInstall: params.trustedSourceLinkedOfficialInstall,
    packageName: parsedSpec.name,
    dependencySpec: resolveManagedNpmRootDependencySpec({
      parsedSpec,
      resolution: npmResolution,
    }),
    displaySpec: spec,
    installPolicyRequest: {
      kind: "plugin-npm",
      requestedSpecifier: spec,
    },
    extensionsDir: params.extensionsDir,
    npmDir: params.npmDir,
    timeoutMs,
    logger,
    mode,
    dryRun,
    expectedPluginId,
    npmResolution,
    ...(driftResult.integrityDrift ? { integrityDrift: driftResult.integrityDrift } : {}),
  });
}

export async function installPluginFromNpmPackArchive(
  params: InstallSafetyOverrides & {
    archivePath: string;
    extensionsDir?: string;
    npmDir?: string;
    timeoutMs?: number;
    logger?: PluginInstallLogger;
    mode?: "install" | "update";
    dryRun?: boolean;
    expectedPluginId?: string;
    expectedIntegrity?: string;
    onIntegrityDrift?: (params: PluginNpmIntegrityDriftParams) => boolean | Promise<boolean>;
  },
): Promise<InstallPluginResult & { npmTarballName?: string }> {
  const runtime = await loadPluginInstallRuntime();
  const { logger, timeoutMs, mode, dryRun } = runtime.resolveTimedInstallModeOptions(
    params,
    defaultLogger,
  );
  const metadataResult = await resolveNpmPackArchiveMetadata({
    archivePath: params.archivePath,
    timeoutMs,
  });
  if (!metadataResult.ok) {
    return metadataResult;
  }
  const npmResolution: NpmSpecResolution = {
    ...metadataResult.metadata,
    resolvedAt: new Date().toISOString(),
  };
  const driftResult = await resolveNpmIntegrityDriftWithDefaultMessage({
    spec: metadataResult.archivePath,
    expectedIntegrity: params.expectedIntegrity,
    resolution: npmResolution,
    onIntegrityDrift: params.onIntegrityDrift,
    warn: (message) => logger.warn?.(message),
  });
  if (driftResult.error) {
    return { ok: false, error: driftResult.error };
  }
  const packageNameResult = resolveTrustedNpmPackPackageName(metadataResult.metadata.name);
  if (!packageNameResult.ok) {
    return packageNameResult;
  }
  const packageName = packageNameResult.packageName;
  const npmBaseDir = params.npmDir ? resolveUserPath(params.npmDir) : resolveDefaultPluginNpmDir();
  const npmRoot = resolvePluginNpmProjectDir({
    npmDir: npmBaseDir,
    packageName,
  });
  let dependencySpec = "";
  if (!dryRun) {
    try {
      dependencySpec = (
        await stageNpmPackArchiveInManagedRoot({
          archivePath: metadataResult.archivePath,
          npmRoot,
          packageName,
          version: metadataResult.metadata.version,
          integrity: metadataResult.metadata.integrity,
          shasum: metadataResult.metadata.shasum,
          tarballName: metadataResult.tarballName,
        })
      ).dependencySpec;
    } catch (error) {
      return {
        ok: false,
        error: `Failed to stage npm pack archive in managed npm root: ${String(error)}`,
      };
    }
  }

  const result = await installPluginFromManagedNpmRoot({
    dangerouslyForceUnsafeInstall: params.dangerouslyForceUnsafeInstall,
    trustedSourceLinkedOfficialInstall: params.trustedSourceLinkedOfficialInstall,
    packageName,
    dependencySpec,
    displaySpec: metadataResult.archivePath,
    installPolicyRequest: {
      kind: "plugin-npm",
      requestedSpecifier: `npm-pack:${metadataResult.archivePath}`,
    },
    extensionsDir: params.extensionsDir,
    npmDir: npmBaseDir,
    timeoutMs,
    logger,
    mode,
    dryRun,
    expectedPluginId: params.expectedPluginId,
    npmResolution,
    ...(driftResult.integrityDrift ? { integrityDrift: driftResult.integrityDrift } : {}),
  });
  return {
    ...result,
    ...(result.ok ? { npmTarballName: metadataResult.tarballName } : {}),
  };
}

export async function installPluginFromPath(
  params: {
    path: string;
  } & PackageInstallCommonParams,
): Promise<InstallPluginResult> {
  const runtime = await loadPluginInstallRuntime();
  const pathResult = await runtime.resolveExistingInstallPath(params.path);
  if (!pathResult.ok) {
    return pathResult;
  }
  const { resolvedPath: resolved, stat } = pathResult;
  const packageInstallOptions = pickPackageInstallCommonParams(params);

  if (stat.isDirectory()) {
    return await installPluginFromDir({
      dirPath: resolved,
      ...packageInstallOptions,
      installPolicyRequest: {
        kind: "plugin-dir",
        requestedSpecifier: params.path,
      },
    });
  }

  const archiveKind = runtime.resolveArchiveKind(resolved);
  if (archiveKind) {
    return await installPluginFromArchive({
      archivePath: resolved,
      ...packageInstallOptions,
      installPolicyRequest: {
        kind: "plugin-archive",
        requestedSpecifier: params.path,
      },
    });
  }

  return await installPluginFromFile({
    filePath: resolved,
    ...pickFileInstallCommonParams({
      ...params,
      installPolicyRequest: {
        kind: "plugin-file",
        requestedSpecifier: params.path,
      },
    }),
  });
}
