Files
openclaw/docs/plugins/dependency-resolution.md
Mason Huang 1c46fe72c9 docs: clarify plugin package live proof (#99962)
Summary:
- The branch adds plugin package live-proof guidance to the testing skill and plugin docs, separating npm-pack package validation from official-trust proof and documenting runtime dependency and compiled-entry checks.
- PR surface: Docs +111. Total +111 across 3 files.
- Reproducibility: not applicable. this is a documentation and skill-guidance clarification PR rather than a bug report. I validated the documented claims against current source, adjacent docs, tests, and live PR checks.

Automerge notes:
- No ClawSweeper repair was needed after automerge opt-in.

Validation:
- ClawSweeper review passed for head b56efcffc5.
- Required merge gates passed before the squash merge.

Prepared head SHA: b56efcffc5
Review: https://github.com/openclaw/openclaw/pull/99962#issuecomment-4882084565

Co-authored-by: Mason Huang <masonxhuang@tencent.com>
Approved-by: hxy91819
2026-07-04 13:41:46 +00:00

9.9 KiB

summary, read_when, title, sidebarTitle
summary read_when title sidebarTitle
How OpenClaw installs plugin packages and resolves plugin dependencies
You are debugging plugin package installs
You are changing plugin startup, doctor, or package-manager install behavior
You are maintaining packaged OpenClaw installs or bundled plugin manifests
Plugin dependency resolution Dependencies

OpenClaw keeps plugin dependency work at install/update time. Runtime loading does not run package managers, repair dependency trees, or mutate the OpenClaw package directory.

Responsibility split

Plugin packages own their dependency graph:

  • runtime dependencies live in the plugin package dependencies or optionalDependencies
  • SDK/core imports are peer or supplied OpenClaw imports
  • local development plugins bring their own already-installed dependencies
  • npm and git plugins are installed into OpenClaw-owned package roots

OpenClaw owns only the plugin lifecycle:

  • discover the plugin source
  • install or update the package when explicitly requested
  • record the install metadata
  • load the plugin entrypoint
  • fail with an actionable error when dependencies are missing

Install roots

OpenClaw uses stable per-source roots:

  • npm packages install into per-plugin projects under ~/.openclaw/npm/projects/<encoded-package>
  • git packages clone under ~/.openclaw/git
  • local/path/archive installs are copied or referenced without dependency repair

npm installs run in that per-plugin project root with:

cd ~/.openclaw/npm/projects/<encoded-package>
npm install --omit=dev --omit=peer --legacy-peer-deps --ignore-scripts --no-audit --no-fund

openclaw plugins install npm-pack:<path.tgz> uses that same per-plugin npm project root for a local npm-pack tarball. OpenClaw reads the tarball's npm metadata, adds it to the managed project as a copied file: dependency, runs the normal npm install, and then verifies the installed lockfile metadata before trusting the plugin. This is intended for package-acceptance and release-candidate proof where a local pack artifact should behave like the registry artifact it simulates.

Use npm-pack: when testing official or external plugin packages before publish. A raw archive or path install is useful for local debugging, but it does not prove the same dependency path as an installed npm or ClawHub package. npm-pack: proves the managed package install shape; it is not, by itself, proof that the plugin is catalog-linked official content.

When behavior depends on bundled-plugin or trusted official plugin status, pair the local package proof with a catalog-backed official install or a published package path that records official trust. Privileged helper access and trusted-official scope handling should be validated on that trusted install path, not inferred from a local tarball install.

If a plugin fails at runtime with a missing import, fix the package manifest instead of repairing the managed project by hand. Runtime imports belong in the plugin package dependencies or optionalDependencies; devDependencies are not installed for managed runtime projects. A local npm install inside ~/.openclaw/npm/projects/<encoded-package> can unblock a temporary diagnostic, but it is not package-acceptance proof because the next install or update will recreate the project from package metadata.

npm may hoist transitive dependencies to the per-plugin project's node_modules beside the plugin package. OpenClaw scans the managed project root before trusting the install and removes that project during uninstall, so hoisted runtime dependencies stay inside that plugin's cleanup boundary.

Published npm plugin packages can ship npm-shrinkwrap.json. npm uses that publishable lockfile during install, and OpenClaw's managed npm project root supports it through the normal npm install path. OpenClaw-owned publishable plugin packages must include a package-local shrinkwrap generated from that plugin package's published dependency graph:

pnpm deps:shrinkwrap:generate
pnpm deps:shrinkwrap:check

The generator strips plugin devDependencies, applies the workspace override policy, and writes extensions/<id>/npm-shrinkwrap.json for each publishToNpm plugin. Third-party plugin packages may also ship shrinkwrap; OpenClaw does not require it for community packages, but npm will respect it when present.

Before treating a local package as release-candidate proof, inspect the tarball that will be installed:

npm pack --pack-destination /tmp
tar -xOf /tmp/<plugin-package>.tgz package/package.json
tar -tf /tmp/<plugin-package>.tgz | grep '^package/dist/'

For dependency changes, also verify a production install can resolve the runtime packages without dev dependencies:

tmpdir=$(mktemp -d)
(
  cd "$tmpdir"
  npm init -y >/dev/null
  npm install --package-lock-only --omit=dev --omit=peer --legacy-peer-deps --ignore-scripts /tmp/<plugin-package>.tgz
)
rm -rf "$tmpdir"

OpenClaw-owned npm plugin packages can also publish with explicit bundledDependencies. The npm publish path overlays the runtime dependency name list, removes dev-only workspace metadata from the published package manifest, runs a script-free npm install for package-local runtime dependencies, then packs or publishes the plugin tarball with those dependency files included. Native-heavy packages, including Codex and ACP runtimes, opt out with openclaw.release.bundleRuntimeDependencies: false; those packages still ship their shrinkwrap, but npm resolves runtime dependencies during install instead of embedding every platform binary in the plugin tarball. The root openclaw package does not bundle its full dependency tree.

Plugins that import openclaw/plugin-sdk/* declare openclaw as a peer dependency. OpenClaw does not let npm install a separate registry copy of the host package into a managed project, because stale host packages can affect npm peer resolution inside that plugin. Managed npm installs skip npm peer resolution/materialization and OpenClaw reasserts plugin-local node_modules/openclaw links for installed packages that declare the host peer after install or update.

git installs clone or refresh the repository, then run:

npm install --omit=dev --ignore-scripts --no-audit --no-fund

The installed plugin then loads from that package directory, so package-local and parent node_modules resolution works the same way it does for a normal Node package.

Local plugins

Local plugins are treated as developer-controlled directories. OpenClaw does not run npm install, pnpm install, or dependency repair for them. If a local plugin has dependencies, install them in that plugin before loading it.

Third-party TypeScript local plugins can use the emergency Jiti path. Packaged JavaScript plugins and bundled internal plugins load through native import/require instead of Jiti.

Startup and reload

Gateway startup and config reload never install plugin dependencies. They read the plugin install records, compute the entrypoint, and load it.

If a dependency is missing at runtime, the plugin fails to load and the error should point the operator to an explicit fix:

openclaw plugins update <id>
openclaw plugins install <source>
openclaw doctor --fix

doctor --fix can clean legacy OpenClaw-generated dependency state and recover downloadable plugins that are missing from the local install records when config references them. Doctor does not repair dependencies for an already-installed local plugin.

Bundled plugins

Lightweight and core-critical bundled plugins are shipped as part of OpenClaw. They should either have no heavy runtime dependency tree or be moved out to a downloadable package on ClawHub/npm.

For the current generated list of plugins that ship in the core package, install externally, or stay source-only, see Plugin inventory.

Bundled plugin manifests must not request dependency staging. Large or optional plugin functionality should be packaged as a normal plugin and installed through the same npm/git/ClawHub path as third-party plugins.

In source checkouts, OpenClaw treats the repository as a pnpm monorepo. After pnpm install, bundled plugins load from extensions/<id> so package-local workspace dependencies are available and edits are picked up directly. Source checkout development is pnpm-only; plain npm install at the repository root is not a supported way to prepare bundled plugin dependencies.

Install shape Bundled plugin location Dependency owner
npm install -g openclaw Built runtime tree inside the package OpenClaw package and explicit plugin install/update/doctor flows
Git checkout plus pnpm install extensions/<id> workspace packages The pnpm workspace, including each plugin package's own dependencies
openclaw plugins install ... Managed npm project/git/ClawHub root The plugin install/update flow

Legacy cleanup

Older OpenClaw versions generated bundled-plugin dependency roots at startup or during doctor repair. Current doctor cleanup removes those stale directories and symlinks when --fix is used, including old plugin-runtime-deps roots, global Node-prefix package symlinks that point at pruned plugin-runtime-deps targets, .openclaw-runtime-deps* manifests, generated plugin node_modules, install stage directories, and package-local pnpm stores. Packaged postinstall also removes those global symlinks before pruning the legacy target roots so upgrades do not leave dangling ESM package imports.

Older npm installs also used a shared ~/.openclaw/npm/node_modules root. Current install, update, uninstall, and doctor flows still recognize that legacy flat root only for recovery and cleanup. New npm installs should create per-plugin project roots instead.