Jump to content

Connect Leeroopedia MCP: Equip your AI agents to search best practices, build plans, verify code, diagnose failures, and look up hyperparameter defaults.

Implementation:Openclaw Openclaw InstallPluginFromNpmSpec

From Leeroopedia


InstallPluginFromNpmSpec and InstallPluginFromPath

installPluginFromNpmSpec and installPluginFromPath are the two primary public entry points for installing OpenClaw plugins. The npm installer fetches a package from the npm registry via npm pack and delegates to the archive installer. The path installer auto-detects whether the source is a directory, an archive, or a single file and delegates to the appropriate sub-installer.

Principle:Openclaw_Openclaw_Plugin_Installation

Source Location

Function File Lines
installPluginFromNpmSpec src/plugins/install.ts 453-505
installPluginFromPath src/plugins/install.ts 507-554

Repository: github.com/openclaw/openclaw

installPluginFromNpmSpec

Signature

export async function installPluginFromNpmSpec(params: {
  spec: string;
  extensionsDir?: string;
  timeoutMs?: number;
  logger?: PluginInstallLogger;
  mode?: "install" | "update";
  dryRun?: boolean;
  expectedPluginId?: string;
}): Promise<InstallPluginResult>

Parameters

Parameter Type Default Description
spec string (required) npm package specifier (e.g., "@openclaw/voice-call", "my-plugin@1.2.3").
extensionsDir string? ~/.openclaw/extensions Custom directory for plugin installation.
timeoutMs number? 120000 Timeout for the npm pack command (minimum 300000ms enforced internally).
logger PluginInstallLogger? no-op logger Logger for progress and warning messages.
mode "update" "install" Whether to fail on existing install or replace it.
dryRun boolean? false When true, validates without writing files.
expectedPluginId string? undefined If set, installation fails when the derived plugin ID does not match.

Return Value

Returns Promise<InstallPluginResult>:

export type InstallPluginResult =
  | {
      ok: true;
      pluginId: string;
      targetDir: string;
      manifestName?: string;
      version?: string;
      extensions: string[];
    }
  | { ok: false; error: string };

Behavior

  1. Validates that the spec is non-empty.
  2. Creates a temp directory and runs npm pack <spec> with COREPACK_ENABLE_DOWNLOAD_PROMPT=0.
  3. Extracts the last line of stdout as the packed archive filename.
  4. Delegates to installPluginFromArchive() with the resolved archive path.

Error Conditions

  • Empty spec: returns { ok: false, error: "missing npm spec" }.
  • npm pack non-zero exit: returns the stderr/stdout as the error.
  • No archive produced: returns { ok: false, error: "npm pack produced no archive" }.

installPluginFromPath

Signature

export async function installPluginFromPath(params: {
  path: string;
  extensionsDir?: string;
  timeoutMs?: number;
  logger?: PluginInstallLogger;
  mode?: "install" | "update";
  dryRun?: boolean;
  expectedPluginId?: string;
}): Promise<InstallPluginResult>

Parameters

Parameter Type Default Description
path string (required) Filesystem path to a directory, archive file, or single extension file.
extensionsDir string? ~/.openclaw/extensions Custom directory for plugin installation.
timeoutMs number? 120000 Timeout for dependency installation.
logger PluginInstallLogger? no-op logger Logger for progress messages.
mode "update" "install" Whether to fail on existing install or replace it.
dryRun boolean? false When true, validates without writing files.
expectedPluginId string? undefined If set, installation fails when the derived plugin ID does not match.

Behavior

  1. Resolves the path via resolveUserPath() and checks existence.
  2. If the path is a directory: delegates to installPluginFromDir().
  3. If the path is a recognized archive (.tar.gz, .tgz, .zip): delegates to installPluginFromArchive().
  4. Otherwise, treats it as a single file: delegates to installPluginFromFile().

Error Conditions

  • Path does not exist: returns { ok: false, error: "path not found: ..." }.

Internal Helpers

Both public functions depend on the shared internal pipeline in installPluginFromPackageDir() (lines 135-309), which handles:

Step Description
Manifest validation Reads package.json, verifies openclaw.extensions is a non-empty array.
Plugin ID derivation Extracts unscoped package name via unscopedPackageName().
Path traversal prevention Uses isPathInside() and resolveSafeInstallDir() to ensure all paths stay within bounds.
Security scanning Calls scanDirectoryWithSummary() with forced scan entries for extension files.
File copy Recursively copies the package to the target directory.
Dependency resolution Runs npm install --omit=dev --silent if the package has dependencies.
Backup/rollback In update mode, creates a timestamped backup and restores it on failure.

Supporting Types

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

Usage Example

// Install from npm
const result = await installPluginFromNpmSpec({
  spec: "@openclaw/voice-call",
  mode: "install",
  logger: { info: console.log, warn: console.warn },
});

// Install from local path (auto-detects type)
const result2 = await installPluginFromPath({
  path: "./my-plugin",
  mode: "update",
  dryRun: true,
});

Related Pages

Requires Environment

Uses Heuristic

Page Connections

Double-click a node to navigate. Hold to expand connections.
Principle
Implementation
Heuristic
Environment