docs(plugins): add generated plugin inventory

This commit is contained in:
Peter Steinberger
2026-05-02 09:39:29 +01:00
parent 5eabb6e697
commit 52a2d38629
5 changed files with 482 additions and 1 deletions

View File

@@ -1182,6 +1182,7 @@
"pages": [
"tools/plugin",
"plugins/community",
"plugins/plugin-inventory",
"plugins/bundles",
"plugins/dependency-resolution",
"plugins/codex-harness",

View File

@@ -95,6 +95,9 @@ 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](/plugins/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.

View File

@@ -0,0 +1,157 @@
---
summary: "Generated inventory of OpenClaw plugins shipped in core, published externally, or kept source-only"
read_when:
- You are deciding whether a plugin ships in the core npm package or installs separately
- You are updating bundled plugin package metadata or release automation
- You need the canonical internal vs external plugin list
title: "Plugin inventory"
---
# Plugin inventory
This page is generated from `extensions/*/package.json`, `openclaw.plugin.json`,
and the root npm package `files` exclusions. Regenerate it with:
```bash
pnpm plugins:inventory:gen
```
## Definitions
- **Core npm package:** built into the `openclaw` npm package and available without a separate plugin install.
- **Official external package:** OpenClaw-maintained plugin omitted from the core npm package and installed through ClawHub and/or npm.
- **Source checkout only:** repo-local plugin omitted from published npm artifacts and not advertised as an installable package.
Source checkouts are different from npm installs: after `pnpm install`, bundled
plugins load from `extensions/<id>` so local edits and package-local workspace
dependencies are available.
## Core npm package
| Plugin | Package | Surface | Docs | Install |
| --------------------- | ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------- | -------------------- |
| alibaba | `@openclaw/alibaba-provider` | contracts: videoGenerationProviders | [alibaba](/providers/alibaba) | included in OpenClaw |
| amazon-bedrock | `@openclaw/amazon-bedrock-provider` | providers: amazon-bedrock; contracts: memoryEmbeddingProviders | [amazon-bedrock](/providers/bedrock) | included in OpenClaw |
| amazon-bedrock-mantle | `@openclaw/amazon-bedrock-mantle-provider` | providers: amazon-bedrock-mantle | [amazon-bedrock-mantle](/providers/bedrock-mantle) | included in OpenClaw |
| anthropic | `@openclaw/anthropic-provider` | providers: anthropic; contracts: mediaUnderstandingProviders | [anthropic](/providers/anthropic) | included in OpenClaw |
| anthropic-vertex | `@openclaw/anthropic-vertex-provider` | providers: anthropic-vertex | - | included in OpenClaw |
| arcee | `@openclaw/arcee-provider` | providers: arcee | [arcee](/providers/arcee) | included in OpenClaw |
| azure-speech | `@openclaw/azure-speech` | contracts: speechProviders | [azure-speech](/providers/azure-speech) | included in OpenClaw |
| bonjour | `@openclaw/bonjour` | plugin | - | included in OpenClaw |
| browser | `@openclaw/browser-plugin` | contracts: tools; skills | [browser](/tools/browser) | included in OpenClaw |
| byteplus | `@openclaw/byteplus-provider` | providers: byteplus, byteplus-plan; contracts: videoGenerationProviders | - | included in OpenClaw |
| cerebras | `@openclaw/cerebras-provider` | providers: cerebras | [cerebras](/providers/cerebras) | included in OpenClaw |
| chutes | `@openclaw/chutes-provider` | providers: chutes | [chutes](/providers/chutes) | included in OpenClaw |
| cloudflare-ai-gateway | `@openclaw/cloudflare-ai-gateway-provider` | providers: cloudflare-ai-gateway | [cloudflare-ai-gateway](/providers/cloudflare-ai-gateway) | included in OpenClaw |
| comfy | `@openclaw/comfy-provider` | providers: comfy; contracts: imageGenerationProviders, musicGenerationProviders, videoGenerationProviders | [comfy](/providers/comfy) | included in OpenClaw |
| copilot-proxy | `@openclaw/copilot-proxy` | providers: copilot-proxy | - | included in OpenClaw |
| deepgram | `@openclaw/deepgram-provider` | contracts: mediaUnderstandingProviders, realtimeTranscriptionProviders | [deepgram](/providers/deepgram) | included in OpenClaw |
| deepinfra | `@openclaw/deepinfra-provider` | providers: deepinfra; contracts: imageGenerationProviders, mediaUnderstandingProviders, memoryEmbeddingProviders, speechProviders, videoGenerationProviders | [deepinfra](/providers/deepinfra) | included in OpenClaw |
| deepseek | `@openclaw/deepseek-provider` | providers: deepseek | [deepseek](/providers/deepseek) | included in OpenClaw |
| document-extract | `@openclaw/document-extract-plugin` | contracts: documentExtractors | [document-extract](/tools/pdf) | included in OpenClaw |
| duckduckgo | `@openclaw/duckduckgo-plugin` | contracts: webSearchProviders | [duckduckgo](/tools/duckduckgo-search) | included in OpenClaw |
| elevenlabs | `@openclaw/elevenlabs-speech` | contracts: mediaUnderstandingProviders, realtimeTranscriptionProviders, speechProviders | [elevenlabs](/providers/elevenlabs) | included in OpenClaw |
| exa | `@openclaw/exa-plugin` | contracts: webSearchProviders | [exa](/tools/exa-search) | included in OpenClaw |
| fal | `@openclaw/fal-provider` | providers: fal; contracts: imageGenerationProviders, videoGenerationProviders | [fal](/providers/fal) | included in OpenClaw |
| file-transfer | `@openclaw/file-transfer` | contracts: tools | - | included in OpenClaw |
| firecrawl | `@openclaw/firecrawl-plugin` | contracts: tools, webFetchProviders, webSearchProviders | [firecrawl](/tools/firecrawl) | included in OpenClaw |
| fireworks | `@openclaw/fireworks-provider` | providers: fireworks | [fireworks](/providers/fireworks) | included in OpenClaw |
| github-copilot | `@openclaw/github-copilot-provider` | providers: github-copilot; contracts: memoryEmbeddingProviders | [github-copilot](/providers/github-copilot) | included in OpenClaw |
| google | `@openclaw/google-plugin` | providers: google, google-gemini-cli, google-vertex; contracts: imageGenerationProviders, mediaUnderstandingProviders, memoryEmbeddingProviders, musicGenerationProviders, realtimeVoiceProviders, speechProviders, videoGenerationProviders, webSearchProviders | [google](/providers/google) | included in OpenClaw |
| gradium | `@openclaw/gradium-speech` | contracts: speechProviders | [gradium](/providers/gradium) | included in OpenClaw |
| groq | `@openclaw/groq-provider` | providers: groq; contracts: mediaUnderstandingProviders | [groq](/providers/groq) | included in OpenClaw |
| huggingface | `@openclaw/huggingface-provider` | providers: huggingface | [huggingface](/providers/huggingface) | included in OpenClaw |
| imessage | `@openclaw/imessage` | channels: imessage | [imessage](/channels/imessage) | included in OpenClaw |
| inworld | `@openclaw/inworld-speech` | contracts: speechProviders | [inworld](/providers/inworld) | included in OpenClaw |
| irc | `@openclaw/irc` | channels: irc | [irc](/channels/irc) | included in OpenClaw |
| kilocode | `@openclaw/kilocode-provider` | providers: kilocode | [kilocode](/providers/kilocode) | included in OpenClaw |
| kimi | `@openclaw/kimi-provider` | providers: kimi, kimi-coding | [kimi](/providers/moonshot) | included in OpenClaw |
| litellm | `@openclaw/litellm-provider` | providers: litellm; contracts: imageGenerationProviders | [litellm](/providers/litellm) | included in OpenClaw |
| llm-task | `@openclaw/llm-task` | contracts: tools | - | included in OpenClaw |
| lmstudio | `@openclaw/lmstudio-provider` | providers: lmstudio; contracts: memoryEmbeddingProviders | [lmstudio](/providers/lmstudio) | included in OpenClaw |
| memory-core | `@openclaw/memory-core` | contracts: memoryEmbeddingProviders, tools | - | included in OpenClaw |
| memory-wiki | `@openclaw/memory-wiki` | contracts: tools; skills | [memory-wiki](/plugins/memory-wiki) | included in OpenClaw |
| microsoft | `@openclaw/microsoft-speech` | contracts: speechProviders | - | included in OpenClaw |
| microsoft-foundry | `@openclaw/microsoft-foundry` | providers: microsoft-foundry | - | included in OpenClaw |
| migrate-claude | `@openclaw/migrate-claude` | contracts: migrationProviders | - | included in OpenClaw |
| migrate-hermes | `@openclaw/migrate-hermes` | contracts: migrationProviders | - | included in OpenClaw |
| minimax | `@openclaw/minimax-provider` | providers: minimax, minimax-portal; contracts: imageGenerationProviders, mediaUnderstandingProviders, musicGenerationProviders, speechProviders, videoGenerationProviders, webSearchProviders | [minimax](/providers/minimax) | included in OpenClaw |
| mistral | `@openclaw/mistral-provider` | providers: mistral; contracts: mediaUnderstandingProviders, memoryEmbeddingProviders, realtimeTranscriptionProviders | [mistral](/providers/mistral) | included in OpenClaw |
| moonshot | `@openclaw/moonshot-provider` | providers: moonshot; contracts: mediaUnderstandingProviders, webSearchProviders | [moonshot](/providers/moonshot) | included in OpenClaw |
| nvidia | `@openclaw/nvidia-provider` | providers: nvidia | [nvidia](/providers/nvidia) | included in OpenClaw |
| ollama | `@openclaw/ollama-provider` | providers: ollama; contracts: memoryEmbeddingProviders, webSearchProviders | [ollama](/providers/ollama) | included in OpenClaw |
| open-prose | `@openclaw/open-prose` | skills | - | included in OpenClaw |
| openai | `@openclaw/openai-provider` | providers: openai, openai-codex; contracts: imageGenerationProviders, mediaUnderstandingProviders, memoryEmbeddingProviders, realtimeTranscriptionProviders, realtimeVoiceProviders, speechProviders, videoGenerationProviders | [openai](/providers/openai) | included in OpenClaw |
| opencode | `@openclaw/opencode-provider` | providers: opencode; contracts: mediaUnderstandingProviders | [opencode](/providers/opencode) | included in OpenClaw |
| opencode-go | `@openclaw/opencode-go-provider` | providers: opencode-go; contracts: mediaUnderstandingProviders | [opencode-go](/providers/opencode-go) | included in OpenClaw |
| openrouter | `@openclaw/openrouter-provider` | providers: openrouter; contracts: imageGenerationProviders, mediaUnderstandingProviders, speechProviders, videoGenerationProviders | [openrouter](/providers/openrouter) | included in OpenClaw |
| openshell | `@openclaw/openshell-sandbox` | plugin | - | included in OpenClaw |
| perplexity | `@openclaw/perplexity-plugin` | contracts: webSearchProviders | [perplexity](/tools/perplexity-search) | included in OpenClaw |
| qianfan | `@openclaw/qianfan-provider` | providers: qianfan | [qianfan](/providers/qianfan) | included in OpenClaw |
| qwen | `@openclaw/qwen-provider` | providers: qwen, qwencloud, modelstudio, dashscope; contracts: mediaUnderstandingProviders, videoGenerationProviders | [qwen](/providers/qwen) | included in OpenClaw |
| runway | `@openclaw/runway-provider` | contracts: videoGenerationProviders | [runway](/providers/runway) | included in OpenClaw |
| searxng | `@openclaw/searxng-plugin` | contracts: webSearchProviders | - | included in OpenClaw |
| senseaudio | `@openclaw/senseaudio-provider` | contracts: mediaUnderstandingProviders | [senseaudio](/providers/senseaudio) | included in OpenClaw |
| sglang | `@openclaw/sglang-provider` | providers: sglang | [sglang](/providers/sglang) | included in OpenClaw |
| signal | `@openclaw/signal` | channels: signal | [signal](/channels/signal) | included in OpenClaw |
| skill-workshop | `@openclaw/skill-workshop` | contracts: tools | [skill-workshop](/plugins/skill-workshop) | included in OpenClaw |
| slack | `@openclaw/slack` | channels: slack | [slack](/channels/slack) | included in OpenClaw |
| stepfun | `@openclaw/stepfun-provider` | providers: stepfun, stepfun-plan | [stepfun](/providers/stepfun) | included in OpenClaw |
| synthetic | `@openclaw/synthetic-provider` | providers: synthetic | [synthetic](/providers/synthetic) | included in OpenClaw |
| tavily | `@openclaw/tavily-plugin` | contracts: tools, webSearchProviders; skills | [tavily](/tools/tavily) | included in OpenClaw |
| telegram | `@openclaw/telegram` | channels: telegram | [telegram](/channels/telegram) | included in OpenClaw |
| tencent | `@openclaw/tencent-provider` | providers: tencent-tokenhub | [tencent](/providers/tencent) | included in OpenClaw |
| together | `@openclaw/together-provider` | providers: together; contracts: videoGenerationProviders | [together](/providers/together) | included in OpenClaw |
| tokenjuice | `@openclaw/tokenjuice` | contracts: agentToolResultMiddleware | [tokenjuice](/tools/tokenjuice) | included in OpenClaw |
| tts-local-cli | `@openclaw/tts-local-cli` | contracts: speechProviders | - | included in OpenClaw |
| venice | `@openclaw/venice-provider` | providers: venice | [venice](/providers/venice) | included in OpenClaw |
| vercel-ai-gateway | `@openclaw/vercel-ai-gateway-provider` | providers: vercel-ai-gateway | [vercel-ai-gateway](/providers/vercel-ai-gateway) | included in OpenClaw |
| vllm | `@openclaw/vllm-provider` | providers: vllm | [vllm](/providers/vllm) | included in OpenClaw |
| volcengine | `@openclaw/volcengine-provider` | providers: volcengine, volcengine-plan; contracts: speechProviders | [volcengine](/providers/volcengine) | included in OpenClaw |
| voyage | `@openclaw/voyage-provider` | contracts: memoryEmbeddingProviders | - | included in OpenClaw |
| vydra | `@openclaw/vydra-provider` | providers: vydra; contracts: imageGenerationProviders, speechProviders, videoGenerationProviders | [vydra](/providers/vydra) | included in OpenClaw |
| web-readability | `@openclaw/web-readability-plugin` | contracts: webContentExtractors | - | included in OpenClaw |
| webhooks | `@openclaw/webhooks` | plugin | [webhooks](/plugins/webhooks) | included in OpenClaw |
| xai | `@openclaw/xai-plugin` | providers: xai; contracts: imageGenerationProviders, mediaUnderstandingProviders, realtimeTranscriptionProviders, speechProviders, tools, videoGenerationProviders, webSearchProviders | [xai](/providers/xai) | included in OpenClaw |
| xiaomi | `@openclaw/xiaomi-provider` | providers: xiaomi; contracts: speechProviders | [xiaomi](/providers/xiaomi) | included in OpenClaw |
| zai | `@openclaw/zai-provider` | providers: zai; contracts: mediaUnderstandingProviders | [zai](/providers/zai) | included in OpenClaw |
## Official external packages
| Plugin | Package | Surface | Docs | Install |
| ---------------------- | ---------------------------------- | ---------------------------------------------------------------------------- | ------------------------------------------------------------- | ------------------------------------------------- |
| acpx | `@openclaw/acpx` | skills | [acpx](/tools/acp-agents-setup) | ClawHub + npm: `@openclaw/acpx` |
| bluebubbles | `@openclaw/bluebubbles` | channels: bluebubbles | [bluebubbles](/channels/bluebubbles) | ClawHub + npm: `@openclaw/bluebubbles` |
| brave | `@openclaw/brave-plugin` | contracts: webSearchProviders | [brave](/tools/brave-search) | ClawHub + npm: `@openclaw/brave-plugin` |
| codex | `@openclaw/codex` | providers: codex; contracts: mediaUnderstandingProviders, migrationProviders | [codex](/plugins/codex-harness) | ClawHub + npm: `@openclaw/codex` |
| diagnostics-otel | `@openclaw/diagnostics-otel` | plugin | - | ClawHub + npm: `@openclaw/diagnostics-otel` |
| diagnostics-prometheus | `@openclaw/diagnostics-prometheus` | plugin | - | ClawHub + npm: `@openclaw/diagnostics-prometheus` |
| diffs | `@openclaw/diffs` | contracts: tools; skills | - | ClawHub + npm: `@openclaw/diffs` |
| discord | `@openclaw/discord` | channels: discord | [discord](/channels/discord) | ClawHub + npm: `@openclaw/discord` |
| feishu | `@openclaw/feishu` | channels: feishu; contracts: tools; skills | [feishu](/channels/feishu) | ClawHub + npm: `@openclaw/feishu` |
| google-meet | `@openclaw/google-meet` | contracts: tools | [google-meet](/plugins/google-meet) | ClawHub + npm: `@openclaw/google-meet` |
| googlechat | `@openclaw/googlechat` | channels: googlechat | [googlechat](/channels/googlechat) | ClawHub + npm: `@openclaw/googlechat` |
| line | `@openclaw/line` | channels: line | [line](/channels/line) | ClawHub + npm: `@openclaw/line` |
| lobster | `@openclaw/lobster` | contracts: tools | - | ClawHub + npm: `@openclaw/lobster` |
| matrix | `@openclaw/matrix` | channels: matrix | [matrix](/channels/matrix) | ClawHub + npm: `@openclaw/matrix` |
| mattermost | `@openclaw/mattermost` | channels: mattermost | [mattermost](/channels/mattermost) | ClawHub + npm: `@openclaw/mattermost` |
| memory-lancedb | `@openclaw/memory-lancedb` | contracts: tools | [memory-lancedb](/plugins/memory-lancedb) | ClawHub + npm: `@openclaw/memory-lancedb` |
| msteams | `@openclaw/msteams` | channels: msteams | [msteams](/channels/msteams) | ClawHub + npm: `@openclaw/msteams` |
| nextcloud-talk | `@openclaw/nextcloud-talk` | channels: nextcloud-talk | [nextcloud-talk](/channels/nextcloud-talk) | ClawHub + npm: `@openclaw/nextcloud-talk` |
| nostr | `@openclaw/nostr` | channels: nostr | [nostr](/channels/nostr) | ClawHub + npm: `@openclaw/nostr` |
| qqbot | `@openclaw/qqbot` | channels: qqbot; contracts: tools; skills | [qqbot](/channels/qqbot) | ClawHub + npm: `@openclaw/qqbot` |
| synology-chat | `@openclaw/synology-chat` | channels: synology-chat | [synology-chat](/channels/synology-chat) | ClawHub + npm: `@openclaw/synology-chat` |
| tlon | `@openclaw/tlon` | channels: tlon; contracts: tools; skills | [tlon](/channels/tlon) | ClawHub + npm: `@openclaw/tlon` |
| twitch | `@openclaw/twitch` | channels: twitch | [twitch](/channels/twitch) | ClawHub + npm: `@openclaw/twitch` |
| voice-call | `@openclaw/voice-call` | contracts: tools | [voice-call](/plugins/voice-call) | ClawHub + npm: `@openclaw/voice-call` |
| whatsapp | `@openclaw/whatsapp` | channels: whatsapp | [whatsapp](/channels/whatsapp) | ClawHub + npm: `@openclaw/whatsapp` |
| zalo | `@openclaw/zalo` | channels: zalo | [zalo](/channels/zalo) | ClawHub + npm: `@openclaw/zalo` |
| zalouser | `@openclaw/zalouser` | channels: zalouser; contracts: tools | [zalouser](/channels/zalouser), [zalouser](/plugins/zalouser) | ClawHub + npm: `@openclaw/zalouser` |
## Source checkout only
| Plugin | Package | Surface | Docs | Install |
| ---------- | ---------------------- | -------------------- | ---------------------------------- | -------------------- |
| qa-channel | `@openclaw/qa-channel` | channels: qa-channel | [qa-channel](/channels/qa-channel) | source checkout only |
| qa-lab | `@openclaw/qa-lab` | plugin | - | source checkout only |
| qa-matrix | `@openclaw/qa-matrix` | plugin | - | source checkout only |

View File

@@ -1438,6 +1438,8 @@
"plugins:boundary-report:ci": "node --import tsx scripts/plugin-boundary-report.ts --summary --fail-on-cross-owner --fail-on-unclassified-unused-reserved --fail-on-eligible-compat",
"plugins:boundary-report:json": "node --import tsx scripts/plugin-boundary-report.ts --json",
"plugins:boundary-report:summary": "node --import tsx scripts/plugin-boundary-report.ts --summary",
"plugins:inventory:check": "node scripts/generate-plugin-inventory-doc.mjs --check",
"plugins:inventory:gen": "node scripts/generate-plugin-inventory-doc.mjs --write",
"plugins:sync": "node --import tsx scripts/sync-plugin-versions.ts",
"plugins:sync:check": "node --import tsx scripts/sync-plugin-versions.ts --check",
"postinstall": "node scripts/postinstall-bundled-plugins.mjs",
@@ -1462,7 +1464,7 @@
"qa:lab:watch": "vite build --watch --config extensions/qa-lab/web/vite.config.ts",
"qa:otel:smoke": "node --import tsx scripts/qa-otel-smoke.ts",
"release-metadata:check": "node scripts/check-release-metadata-only.mjs",
"release:check": "pnpm deps:root-ownership:check && pnpm check:base-config-schema && pnpm check:bundled-channel-config-metadata && pnpm config:docs:check && pnpm plugin-sdk:check-exports && pnpm plugin-sdk:api:check && node --import tsx scripts/release-check.ts",
"release:check": "pnpm deps:root-ownership:check && pnpm plugins:inventory:check && pnpm check:base-config-schema && pnpm check:bundled-channel-config-metadata && pnpm config:docs:check && pnpm plugin-sdk:check-exports && pnpm plugin-sdk:api:check && node --import tsx scripts/release-check.ts",
"release:openclaw:npm:check": "node --import tsx scripts/openclaw-npm-release-check.ts",
"release:openclaw:npm:verify-published": "node --import tsx scripts/openclaw-npm-postpublish-verify.ts",
"release:plugins:clawhub:check": "node --import tsx scripts/plugin-clawhub-release-check.ts",

View File

@@ -0,0 +1,318 @@
#!/usr/bin/env node
import fs from "node:fs";
import path from "node:path";
import process from "node:process";
const DOC_PATH = "docs/plugins/plugin-inventory.md";
const ROOT = process.cwd();
const EXTENSIONS_DIR = path.join(ROOT, "extensions");
const PROVIDER_DOC_ALIASES = new Map([
["amazon-bedrock", "/providers/bedrock"],
["amazon-bedrock-mantle", "/providers/bedrock-mantle"],
["kimi", "/providers/moonshot"],
["perplexity", "/providers/perplexity-provider"],
]);
const PLUGIN_DOC_ALIASES = new Map([
["acpx", "/tools/acp-agents-setup"],
["brave", "/tools/brave-search"],
["browser", "/tools/browser"],
["codex", "/plugins/codex-harness"],
["document-extract", "/tools/pdf"],
["duckduckgo", "/tools/duckduckgo-search"],
["exa", "/tools/exa-search"],
["firecrawl", "/tools/firecrawl"],
["perplexity", "/tools/perplexity-search"],
["tavily", "/tools/tavily"],
["tokenjuice", "/tools/tokenjuice"],
]);
function readJson(relativePath) {
return JSON.parse(fs.readFileSync(path.join(ROOT, relativePath), "utf8"));
}
function fileExists(relativePath) {
return fs.existsSync(path.join(ROOT, relativePath));
}
function collectExcludedPackagedExtensionDirs(rootPackageJson) {
const excluded = new Set();
for (const entry of rootPackageJson.files ?? []) {
if (typeof entry !== "string") {
continue;
}
const match = /^!dist\/extensions\/([^/]+)\/\*\*$/u.exec(entry);
if (match?.[1]) {
excluded.add(match[1]);
}
}
return excluded;
}
function normalizeDocPath(value) {
if (typeof value !== "string" || !value.startsWith("/")) {
return null;
}
return value.replace(/\.mdx?$/u, "");
}
function docLink(label, href) {
return `[${label}](${href})`;
}
function pushUnique(values, value) {
if (value && !values.includes(value)) {
values.push(value);
}
}
function resolveDocs({ dirName, manifest, packageJson }) {
const links = [];
const pluginAlias = PLUGIN_DOC_ALIASES.get(manifest.id) ?? PLUGIN_DOC_ALIASES.get(dirName);
if (pluginAlias) {
pushUnique(links, docLink(manifest.id ?? dirName, pluginAlias));
}
const channelDoc = normalizeDocPath(packageJson.openclaw?.channel?.docsPath);
if (channelDoc) {
pushUnique(links, docLink(channelDoc.replace(/^\/channels\//u, ""), channelDoc));
}
for (const channel of manifest.channels ?? []) {
if (typeof channel !== "string") {
continue;
}
const relativePath = `docs/channels/${channel}.md`;
if (fileExists(relativePath)) {
pushUnique(links, docLink(channel, `/channels/${channel}`));
}
}
for (const provider of manifest.providers ?? []) {
if (typeof provider !== "string") {
continue;
}
const alias = PROVIDER_DOC_ALIASES.get(provider);
if (alias) {
pushUnique(links, docLink(provider, alias));
continue;
}
const relativePath = `docs/providers/${provider}.md`;
if (fileExists(relativePath)) {
pushUnique(links, docLink(provider, `/providers/${provider}`));
}
}
for (const candidate of [manifest.id, dirName]) {
if (typeof candidate !== "string") {
continue;
}
if (fileExists(`docs/channels/${candidate}.md`)) {
pushUnique(links, docLink(candidate, `/channels/${candidate}`));
}
if (fileExists(`docs/providers/${candidate}.md`)) {
pushUnique(links, docLink(candidate, `/providers/${candidate}`));
}
if (fileExists(`docs/plugins/${candidate}.md`)) {
pushUnique(links, docLink(candidate, `/plugins/${candidate}`));
}
}
return links.length > 0 ? links.join(", ") : "-";
}
function resolveSurface(manifest) {
const parts = [];
if (Array.isArray(manifest.channels) && manifest.channels.length > 0) {
parts.push(`channels: ${manifest.channels.join(", ")}`);
}
if (Array.isArray(manifest.providers) && manifest.providers.length > 0) {
parts.push(`providers: ${manifest.providers.join(", ")}`);
}
const contracts = Object.keys(manifest.contracts ?? {}).toSorted((left, right) =>
left.localeCompare(right),
);
if (contracts.length > 0) {
parts.push(`contracts: ${contracts.join(", ")}`);
}
if (Array.isArray(manifest.skills) && manifest.skills.length > 0) {
parts.push("skills");
}
if (parts.length === 0) {
return "plugin";
}
return parts.join("; ");
}
function resolveInstall(packageJson, status) {
if (status === "source") {
return "source checkout only";
}
if (status === "core") {
return "included in OpenClaw";
}
const install = packageJson.openclaw?.install;
const release = packageJson.openclaw?.release;
if (release?.publishToClawHub === true && release?.publishToNpm === true) {
return install?.npmSpec ? `ClawHub + npm: \`${install.npmSpec}\`` : "ClawHub + npm";
}
if (release?.publishToClawHub === true) {
return install?.npmSpec ? `ClawHub: \`${install.npmSpec}\`` : "ClawHub";
}
if (release?.publishToNpm === true || typeof install?.npmSpec === "string") {
return `npm: \`${install.npmSpec}\``;
}
return "installable plugin";
}
function resolveStatus({ dirName, packageJson, excludedDirs }) {
const release = packageJson.openclaw?.release;
const hasInstallSpec = typeof packageJson.openclaw?.install?.npmSpec === "string";
const excluded =
excludedDirs.has(dirName) || packageJson.openclaw?.bundle?.includeInCore === false;
if (!excluded) {
return "core";
}
if (release?.publishToClawHub === true || release?.publishToNpm === true || hasInstallSpec) {
return "external";
}
return "source";
}
function escapeCell(value) {
return String(value).replaceAll("\n", " ").replaceAll("|", "\\|");
}
function renderTable(records) {
const rows = [
["Plugin", "Package", "Surface", "Docs", "Install"],
...records.map((record) => [
escapeCell(record.id),
`\`${escapeCell(record.packageName)}\``,
escapeCell(record.surface),
escapeCell(record.docs),
escapeCell(record.install),
]),
];
const widths = rows[0].map((_, index) => Math.max(...rows.map((row) => row[index].length), 3));
const lines = [];
lines.push(formatTableRow(rows[0], widths));
lines.push(
formatTableRow(
widths.map((width) => "-".repeat(width)),
widths,
),
);
for (const row of rows.slice(1)) {
lines.push(formatTableRow(row, widths));
}
return lines.join("\n");
}
function formatTableRow(row, widths) {
return `| ${row.map((cell, index) => cell.padEnd(widths[index])).join(" | ")} |`;
}
function collectPluginRecords() {
const rootPackageJson = readJson("package.json");
const excludedDirs = collectExcludedPackagedExtensionDirs(rootPackageJson);
const records = [];
for (const dirName of fs
.readdirSync(EXTENSIONS_DIR)
.toSorted((left, right) => left.localeCompare(right))) {
const packagePath = path.join(EXTENSIONS_DIR, dirName, "package.json");
const manifestPath = path.join(EXTENSIONS_DIR, dirName, "openclaw.plugin.json");
if (!fs.existsSync(packagePath) || !fs.existsSync(manifestPath)) {
continue;
}
const packageJson = JSON.parse(fs.readFileSync(packagePath, "utf8"));
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
const id = typeof manifest.id === "string" && manifest.id ? manifest.id : dirName;
const status = resolveStatus({ dirName, packageJson, excludedDirs });
records.push({
docs: resolveDocs({ dirName, manifest, packageJson }),
id,
install: resolveInstall(packageJson, status),
packageName: packageJson.name ?? "-",
status,
surface: resolveSurface(manifest),
});
}
return records.toSorted((left, right) => left.id.localeCompare(right.id));
}
function renderDocument() {
const records = collectPluginRecords();
const groups = {
core: records.filter((record) => record.status === "core"),
external: records.filter((record) => record.status === "external"),
source: records.filter((record) => record.status === "source"),
};
return `---
summary: "Generated inventory of OpenClaw plugins shipped in core, published externally, or kept source-only"
read_when:
- You are deciding whether a plugin ships in the core npm package or installs separately
- You are updating bundled plugin package metadata or release automation
- You need the canonical internal vs external plugin list
title: "Plugin inventory"
---
# Plugin inventory
This page is generated from \`extensions/*/package.json\`, \`openclaw.plugin.json\`,
and the root npm package \`files\` exclusions. Regenerate it with:
\`\`\`bash
pnpm plugins:inventory:gen
\`\`\`
## Definitions
- **Core npm package:** built into the \`openclaw\` npm package and available without a separate plugin install.
- **Official external package:** OpenClaw-maintained plugin omitted from the core npm package and installed through ClawHub and/or npm.
- **Source checkout only:** repo-local plugin omitted from published npm artifacts and not advertised as an installable package.
Source checkouts are different from npm installs: after \`pnpm install\`, bundled
plugins load from \`extensions/<id>\` so local edits and package-local workspace
dependencies are available.
## Core npm package
${renderTable(groups.core)}
## Official external packages
${renderTable(groups.external)}
## Source checkout only
${renderTable(groups.source)}
`;
}
function main(argv = process.argv.slice(2)) {
const write = argv.includes("--write");
const check = argv.includes("--check");
if (write === check) {
console.error("usage: node scripts/generate-plugin-inventory-doc.mjs --write|--check");
process.exit(2);
}
const next = renderDocument();
const docPath = path.join(ROOT, DOC_PATH);
if (write) {
fs.writeFileSync(docPath, next, "utf8");
return;
}
const current = fs.existsSync(docPath) ? fs.readFileSync(docPath, "utf8") : "";
if (current !== next) {
console.error(`${DOC_PATH} is stale. Run \`pnpm plugins:inventory:gen\`.`);
process.exit(1);
}
}
main();