diff --git a/docs/docs.json b/docs/docs.json index f625eefce5e..84b4144e887 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -1182,6 +1182,7 @@ "pages": [ "tools/plugin", "plugins/community", + "plugins/plugin-inventory", "plugins/bundles", "plugins/dependency-resolution", "plugins/codex-harness", diff --git a/docs/plugins/dependency-resolution.md b/docs/plugins/dependency-resolution.md index e52cb5720f2..7324706d038 100644 --- a/docs/plugins/dependency-resolution.md +++ b/docs/plugins/dependency-resolution.md @@ -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. diff --git a/docs/plugins/plugin-inventory.md b/docs/plugins/plugin-inventory.md new file mode 100644 index 00000000000..ee665148f95 --- /dev/null +++ b/docs/plugins/plugin-inventory.md @@ -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/` 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 | diff --git a/package.json b/package.json index 6b44ac9f2f5..c2baa4a6705 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/scripts/generate-plugin-inventory-doc.mjs b/scripts/generate-plugin-inventory-doc.mjs new file mode 100644 index 00000000000..f94a7c3a145 --- /dev/null +++ b/scripts/generate-plugin-inventory-doc.mjs @@ -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/\` 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();