diff --git a/docs/plugins/sdk-setup.md b/docs/plugins/sdk-setup.md
index a60a5f8f05c..239528b5c77 100644
--- a/docs/plugins/sdk-setup.md
+++ b/docs/plugins/sdk-setup.md
@@ -1,86 +1,92 @@
---
summary: "Setup wizards, setup-entry.ts, config schemas, and package.json metadata"
title: "Plugin setup and config"
-sidebarTitle: "Setup and Config"
+sidebarTitle: "Setup and config"
read_when:
- You are adding a setup wizard to a plugin
- You need to understand setup-entry.ts vs index.ts
- You are defining plugin config schemas or package.json openclaw metadata
---
-Reference for plugin packaging (`package.json` metadata), manifests
-(`openclaw.plugin.json`), setup entries, and config schemas.
+Reference for plugin packaging (`package.json` metadata), manifests (`openclaw.plugin.json`), setup entries, and config schemas.
- **Looking for a walkthrough?** The how-to guides cover packaging in context:
- [Channel Plugins](/plugins/sdk-channel-plugins#step-1-package-and-manifest) and
- [Provider Plugins](/plugins/sdk-provider-plugins#step-1-package-and-manifest).
+**Looking for a walkthrough?** The how-to guides cover packaging in context: [Channel plugins](/plugins/sdk-channel-plugins#step-1-package-and-manifest) and [Provider plugins](/plugins/sdk-provider-plugins#step-1-package-and-manifest).
## Package metadata
-Your `package.json` needs an `openclaw` field that tells the plugin system what
-your plugin provides:
+Your `package.json` needs an `openclaw` field that tells the plugin system what your plugin provides:
-**Channel plugin:**
-
-```json
-{
- "name": "@myorg/openclaw-my-channel",
- "version": "1.0.0",
- "type": "module",
- "openclaw": {
- "extensions": ["./index.ts"],
- "setupEntry": "./setup-entry.ts",
- "channel": {
- "id": "my-channel",
- "label": "My Channel",
- "blurb": "Short description of the channel."
+
+
+ ```json
+ {
+ "name": "@myorg/openclaw-my-channel",
+ "version": "1.0.0",
+ "type": "module",
+ "openclaw": {
+ "extensions": ["./index.ts"],
+ "setupEntry": "./setup-entry.ts",
+ "channel": {
+ "id": "my-channel",
+ "label": "My Channel",
+ "blurb": "Short description of the channel."
+ }
+ }
}
- }
-}
-```
-
-**Provider plugin / ClawHub publish baseline:**
-
-```json openclaw-clawhub-package.json
-{
- "name": "@myorg/openclaw-my-plugin",
- "version": "1.0.0",
- "type": "module",
- "openclaw": {
- "extensions": ["./index.ts"],
- "compat": {
- "pluginApi": ">=2026.3.24-beta.2",
- "minGatewayVersion": "2026.3.24-beta.2"
- },
- "build": {
- "openclawVersion": "2026.3.24-beta.2",
- "pluginSdkVersion": "2026.3.24-beta.2"
+ ```
+
+
+ ```json openclaw-clawhub-package.json
+ {
+ "name": "@myorg/openclaw-my-plugin",
+ "version": "1.0.0",
+ "type": "module",
+ "openclaw": {
+ "extensions": ["./index.ts"],
+ "compat": {
+ "pluginApi": ">=2026.3.24-beta.2",
+ "minGatewayVersion": "2026.3.24-beta.2"
+ },
+ "build": {
+ "openclawVersion": "2026.3.24-beta.2",
+ "pluginSdkVersion": "2026.3.24-beta.2"
+ }
+ }
}
- }
-}
-```
+ ```
+
+
-If you publish the plugin externally on ClawHub, those `compat` and `build`
-fields are required. The canonical publish snippets live in
-`docs/snippets/plugin-publish/`.
+
+If you publish the plugin externally on ClawHub, those `compat` and `build` fields are required. The canonical publish snippets live in `docs/snippets/plugin-publish/`.
+
### `openclaw` fields
-| Field | Type | Description |
-| ------------ | ---------- | --------------------------------------------------------------------------------------------------------------------------- |
-| `extensions` | `string[]` | Entry point files (relative to package root) |
-| `setupEntry` | `string` | Lightweight setup-only entry (optional) |
-| `channel` | `object` | Channel catalog metadata for setup, picker, quickstart, and status surfaces |
-| `providers` | `string[]` | Provider ids registered by this plugin |
-| `install` | `object` | Install hints: `npmSpec`, `localPath`, `defaultChoice`, `minHostVersion`, `expectedIntegrity`, `allowInvalidConfigRecovery` |
-| `startup` | `object` | Startup behavior flags |
+
+ Entry point files (relative to package root).
+
+
+ Lightweight setup-only entry (optional).
+
+
+ Channel catalog metadata for setup, picker, quickstart, and status surfaces.
+
+
+ Provider ids registered by this plugin.
+
+
+ Install hints: `npmSpec`, `localPath`, `defaultChoice`, `minHostVersion`, `expectedIntegrity`, `allowInvalidConfigRecovery`.
+
+
+ Startup behavior flags.
+
### `openclaw.channel`
-`openclaw.channel` is cheap package metadata for channel discovery and setup
-surfaces before runtime loads.
+`openclaw.channel` is cheap package metadata for channel discovery and setup surfaces before runtime loads.
| Field | Type | What it means |
| -------------------------------------- | ---------- | ----------------------------------------------------------------------------- |
@@ -140,8 +146,9 @@ Example:
- `setup`: include the channel in interactive setup/configure pickers
- `docs`: mark the channel as public-facing in docs/navigation surfaces
-`showConfigured` and `showInSetup` remain supported as legacy aliases. Prefer
-`exposure`.
+
+`showConfigured` and `showInSetup` remain supported as legacy aliases. Prefer `exposure`.
+
### `openclaw.install`
@@ -156,39 +163,33 @@ Example:
| `expectedIntegrity` | `string` | Expected npm dist integrity string, usually `sha512-...`, for pinned installs. |
| `allowInvalidConfigRecovery` | `boolean` | Lets bundled-plugin reinstall flows recover from specific stale-config failures. |
-Interactive onboarding also uses `openclaw.install` for install-on-demand
-surfaces. If your plugin exposes provider auth choices or channel setup/catalog
-metadata before runtime loads, onboarding can show that choice, prompt for npm
-vs local install, install or enable the plugin, then continue the selected
-flow. Npm onboarding choices require trusted catalog metadata with a registry
-`npmSpec`; exact versions and `expectedIntegrity` are optional pins. If
-`expectedIntegrity` is present, install/update flows enforce it. Keep the "what
-to show" metadata in `openclaw.plugin.json` and the "how to install it"
-metadata in `package.json`.
+
+
+ Interactive onboarding also uses `openclaw.install` for install-on-demand surfaces. If your plugin exposes provider auth choices or channel setup/catalog metadata before runtime loads, onboarding can show that choice, prompt for npm vs local install, install or enable the plugin, then continue the selected flow. Npm onboarding choices require trusted catalog metadata with a registry `npmSpec`; exact versions and `expectedIntegrity` are optional pins. If `expectedIntegrity` is present, install/update flows enforce it. Keep the "what to show" metadata in `openclaw.plugin.json` and the "how to install it" metadata in `package.json`.
+
+
+ If `minHostVersion` is set, install and manifest-registry loading both enforce it. Older hosts skip the plugin; invalid version strings are rejected.
+
+
+ For pinned npm installs, keep the exact version in `npmSpec` and add the expected artifact integrity:
-If `minHostVersion` is set, install and manifest-registry loading both enforce
-it. Older hosts skip the plugin; invalid version strings are rejected.
-
-For pinned npm installs, keep the exact version in `npmSpec` and add the
-expected artifact integrity:
-
-```json
-{
- "openclaw": {
- "install": {
- "npmSpec": "@wecom/wecom-openclaw-plugin@1.2.3",
- "expectedIntegrity": "sha512-REPLACE_WITH_NPM_DIST_INTEGRITY",
- "defaultChoice": "npm"
+ ```json
+ {
+ "openclaw": {
+ "install": {
+ "npmSpec": "@wecom/wecom-openclaw-plugin@1.2.3",
+ "expectedIntegrity": "sha512-REPLACE_WITH_NPM_DIST_INTEGRITY",
+ "defaultChoice": "npm"
+ }
+ }
}
- }
-}
-```
+ ```
-`allowInvalidConfigRecovery` is not a general bypass for broken configs. It is
-for narrow bundled-plugin recovery only, so reinstall/setup can repair known
-upgrade leftovers like a missing bundled plugin path or stale `channels.`
-entry for that same plugin. If config is broken for unrelated reasons, install
-still fails closed and tells the operator to run `openclaw doctor --fix`.
+
+
+ `allowInvalidConfigRecovery` is not a general bypass for broken configs. It is for narrow bundled-plugin recovery only, so reinstall/setup can repair known upgrade leftovers like a missing bundled plugin path or stale `channels.` entry for that same plugin. If config is broken for unrelated reasons, install still fails closed and tells the operator to run `openclaw doctor --fix`.
+
+
### Deferred full load
@@ -206,26 +207,17 @@ Channel plugins can opt into deferred loading with:
}
```
-When enabled, OpenClaw loads only `setupEntry` during the pre-listen startup
-phase, even for already-configured channels. The full entry loads after the
-gateway starts listening.
+When enabled, OpenClaw loads only `setupEntry` during the pre-listen startup phase, even for already-configured channels. The full entry loads after the gateway starts listening.
- Only enable deferred loading when your `setupEntry` registers everything the
- gateway needs before it starts listening (channel registration, HTTP routes,
- gateway methods). If the full entry owns required startup capabilities, keep
- the default behavior.
+Only enable deferred loading when your `setupEntry` registers everything the gateway needs before it starts listening (channel registration, HTTP routes, gateway methods). If the full entry owns required startup capabilities, keep the default behavior.
-If your setup/full entry registers gateway RPC methods, keep them on a
-plugin-specific prefix. Reserved core admin namespaces (`config.*`,
-`exec.approvals.*`, `wizard.*`, `update.*`) stay core-owned and always resolve
-to `operator.admin`.
+If your setup/full entry registers gateway RPC methods, keep them on a plugin-specific prefix. Reserved core admin namespaces (`config.*`, `exec.approvals.*`, `wizard.*`, `update.*`) stay core-owned and always resolve to `operator.admin`.
## Plugin manifest
-Every native plugin must ship an `openclaw.plugin.json` in the package root.
-OpenClaw uses this to validate config without executing plugin code.
+Every native plugin must ship an `openclaw.plugin.json` in the package root. OpenClaw uses this to validate config without executing plugin code.
```json
{
@@ -272,7 +264,7 @@ Even plugins with no config must ship a schema. An empty schema is valid:
}
```
-See [Plugin Manifest](/plugins/manifest) for the full schema reference.
+See [Plugin manifest](/plugins/manifest) for the full schema reference.
## ClawHub publishing
@@ -283,14 +275,13 @@ clawhub package publish your-org/your-plugin --dry-run
clawhub package publish your-org/your-plugin
```
-The legacy skill-only publish alias is for skills. Plugin packages should
-always use `clawhub package publish`.
+
+The legacy skill-only publish alias is for skills. Plugin packages should always use `clawhub package publish`.
+
## Setup entry
-The `setup-entry.ts` file is a lightweight alternative to `index.ts` that
-OpenClaw loads when it only needs setup surfaces (onboarding, config repair,
-disabled channel inspection).
+The `setup-entry.ts` file is a lightweight alternative to `index.ts` that OpenClaw loads when it only needs setup surfaces (onboarding, config repair, disabled channel inspection).
```typescript
// setup-entry.ts
@@ -300,41 +291,35 @@ import { myChannelPlugin } from "./src/channel.js";
export default defineSetupPluginEntry(myChannelPlugin);
```
-This avoids loading heavy runtime code (crypto libraries, CLI registrations,
-background services) during setup flows.
+This avoids loading heavy runtime code (crypto libraries, CLI registrations, background services) during setup flows.
-Bundled workspace channels that keep setup-safe exports in sidecar modules can
-use `defineBundledChannelSetupEntry(...)` from
-`openclaw/plugin-sdk/channel-entry-contract` instead of
-`defineSetupPluginEntry(...)`. That bundled contract also supports an optional
-`runtime` export so setup-time runtime wiring can stay lightweight and explicit.
+Bundled workspace channels that keep setup-safe exports in sidecar modules can use `defineBundledChannelSetupEntry(...)` from `openclaw/plugin-sdk/channel-entry-contract` instead of `defineSetupPluginEntry(...)`. That bundled contract also supports an optional `runtime` export so setup-time runtime wiring can stay lightweight and explicit.
-**When OpenClaw uses `setupEntry` instead of the full entry:**
+
+
+ - The channel is disabled but needs setup/onboarding surfaces.
+ - The channel is enabled but unconfigured.
+ - Deferred loading is enabled (`deferConfiguredChannelFullLoadUntilAfterListen`).
+
+
+ - The channel plugin object (via `defineSetupPluginEntry`).
+ - Any HTTP routes required before gateway listen.
+ - Any gateway methods needed during startup.
-- The channel is disabled but needs setup/onboarding surfaces
-- The channel is enabled but unconfigured
-- Deferred loading is enabled (`deferConfiguredChannelFullLoadUntilAfterListen`)
+ Those startup gateway methods should still avoid reserved core admin namespaces such as `config.*` or `update.*`.
-**What `setupEntry` must register:**
-
-- The channel plugin object (via `defineSetupPluginEntry`)
-- Any HTTP routes required before gateway listen
-- Any gateway methods needed during startup
-
-Those startup gateway methods should still avoid reserved core admin
-namespaces such as `config.*` or `update.*`.
-
-**What `setupEntry` should NOT include:**
-
-- CLI registrations
-- Background services
-- Heavy runtime imports (crypto, SDKs)
-- Gateway methods only needed after startup
+
+
+ - CLI registrations.
+ - Background services.
+ - Heavy runtime imports (crypto, SDKs).
+ - Gateway methods only needed after startup.
+
+
### Narrow setup helper imports
-For hot setup-only paths, prefer the narrow setup helper seams over the broader
-`plugin-sdk/setup` umbrella when you only need part of the setup surface:
+For hot setup-only paths, prefer the narrow setup helper seams over the broader `plugin-sdk/setup` umbrella when you only need part of the setup surface:
| Import path | Use it for | Key exports |
| ---------------------------------- | ----------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -342,41 +327,27 @@ For hot setup-only paths, prefer the narrow setup helper seams over the broader
| `plugin-sdk/setup-adapter-runtime` | environment-aware account setup adapters | `createEnvPatchedAccountSetupAdapter` |
| `plugin-sdk/setup-tools` | setup/install CLI/archive/docs helpers | `formatCliCommand`, `detectBinary`, `extractArchive`, `resolveBrewExecutable`, `formatDocsLink`, `CONFIG_DIR` |
-Use the broader `plugin-sdk/setup` seam when you want the full shared setup
-toolbox, including config-patch helpers such as
-`moveSingleAccountChannelSectionToDefaultAccount(...)`.
+Use the broader `plugin-sdk/setup` seam when you want the full shared setup toolbox, including config-patch helpers such as `moveSingleAccountChannelSectionToDefaultAccount(...)`.
-The setup patch adapters stay hot-path safe on import. Their bundled
-single-account promotion contract-surface lookup is lazy, so importing
-`plugin-sdk/setup-runtime` does not eagerly load bundled contract-surface
-discovery before the adapter is actually used.
+The setup patch adapters stay hot-path safe on import. Their bundled single-account promotion contract-surface lookup is lazy, so importing `plugin-sdk/setup-runtime` does not eagerly load bundled contract-surface discovery before the adapter is actually used.
### Channel-owned single-account promotion
-When a channel upgrades from a single-account top-level config to
-`channels..accounts.*`, the default shared behavior is to move promoted
-account-scoped values into `accounts.default`.
+When a channel upgrades from a single-account top-level config to `channels..accounts.*`, the default shared behavior is to move promoted account-scoped values into `accounts.default`.
-Bundled channels can narrow or override that promotion through their setup
-contract surface:
+Bundled channels can narrow or override that promotion through their setup contract surface:
-- `singleAccountKeysToMove`: extra top-level keys that should move into the
- promoted account
-- `namedAccountPromotionKeys`: when named accounts already exist, only these
- keys move into the promoted account; shared policy/delivery keys stay at the
- channel root
-- `resolveSingleAccountPromotionTarget(...)`: choose which existing account
- receives promoted values
+- `singleAccountKeysToMove`: extra top-level keys that should move into the promoted account
+- `namedAccountPromotionKeys`: when named accounts already exist, only these keys move into the promoted account; shared policy/delivery keys stay at the channel root
+- `resolveSingleAccountPromotionTarget(...)`: choose which existing account receives promoted values
-Matrix is the current bundled example. If exactly one named Matrix account
-already exists, or if `defaultAccount` points at an existing non-canonical key
-such as `Ops`, promotion preserves that account instead of creating a new
-`accounts.default` entry.
+
+Matrix is the current bundled example. If exactly one named Matrix account already exists, or if `defaultAccount` points at an existing non-canonical key such as `Ops`, promotion preserves that account instead of creating a new `accounts.default` entry.
+
## Config schema
-Plugin config is validated against the JSON Schema in your manifest. Users
-configure plugins via:
+Plugin config is validated against the JSON Schema in your manifest. Users configure plugins via:
```json5
{
@@ -409,8 +380,7 @@ For channel-specific config, use the channel config section instead:
### Building channel config schemas
-Use `buildChannelConfigSchema` to convert a Zod schema into the
-`ChannelConfigSchema` wrapper used by plugin-owned config artifacts:
+Use `buildChannelConfigSchema` to convert a Zod schema into the `ChannelConfigSchema` wrapper used by plugin-owned config artifacts:
```typescript
import { z } from "zod";
@@ -426,15 +396,11 @@ const accountSchema = z.object({
const configSchema = buildChannelConfigSchema(accountSchema);
```
-For third-party plugins, the cold-path contract is still the plugin manifest:
-mirror the generated JSON Schema into `openclaw.plugin.json#channelConfigs` so
-config schema, setup, and UI surfaces can inspect `channels.` without
-loading runtime code.
+For third-party plugins, the cold-path contract is still the plugin manifest: mirror the generated JSON Schema into `openclaw.plugin.json#channelConfigs` so config schema, setup, and UI surfaces can inspect `channels.` without loading runtime code.
## Setup wizards
-Channel plugins can provide interactive setup wizards for `openclaw onboard`.
-The wizard is a `ChannelSetupWizard` object on the `ChannelPlugin`:
+Channel plugins can provide interactive setup wizards for `openclaw onboard`. The wizard is a `ChannelSetupWizard` object on the `ChannelPlugin`:
```typescript
import type { ChannelSetupWizard } from "openclaw/plugin-sdk/channel-setup";
@@ -467,84 +433,75 @@ const setupWizard: ChannelSetupWizard = {
};
```
-The `ChannelSetupWizard` type supports `credentials`, `textInputs`,
-`dmPolicy`, `allowFrom`, `groupAccess`, `prepare`, `finalize`, and more.
-See bundled plugin packages (for example the Discord plugin `src/channel.setup.ts`) for
-full examples.
+The `ChannelSetupWizard` type supports `credentials`, `textInputs`, `dmPolicy`, `allowFrom`, `groupAccess`, `prepare`, `finalize`, and more. See bundled plugin packages (for example the Discord plugin `src/channel.setup.ts`) for full examples.
-For DM allowlist prompts that only need the standard
-`note -> prompt -> parse -> merge -> patch` flow, prefer the shared setup
-helpers from `openclaw/plugin-sdk/setup`: `createPromptParsedAllowFromForAccount(...)`,
-`createTopLevelChannelParsedAllowFromPrompt(...)`, and
-`createNestedChannelParsedAllowFromPrompt(...)`.
+
+
+ For DM allowlist prompts that only need the standard `note -> prompt -> parse -> merge -> patch` flow, prefer the shared setup helpers from `openclaw/plugin-sdk/setup`: `createPromptParsedAllowFromForAccount(...)`, `createTopLevelChannelParsedAllowFromPrompt(...)`, and `createNestedChannelParsedAllowFromPrompt(...)`.
+
+
+ For channel setup status blocks that only vary by labels, scores, and optional extra lines, prefer `createStandardChannelSetupStatus(...)` from `openclaw/plugin-sdk/setup` instead of hand-rolling the same `status` object in each plugin.
+
+
+ For optional setup surfaces that should only appear in certain contexts, use `createOptionalChannelSetupSurface` from `openclaw/plugin-sdk/channel-setup`:
-For channel setup status blocks that only vary by labels, scores, and optional
-extra lines, prefer `createStandardChannelSetupStatus(...)` from
-`openclaw/plugin-sdk/setup` instead of hand-rolling the same `status` object in
-each plugin.
+ ```typescript
+ import { createOptionalChannelSetupSurface } from "openclaw/plugin-sdk/channel-setup";
-For optional setup surfaces that should only appear in certain contexts, use
-`createOptionalChannelSetupSurface` from `openclaw/plugin-sdk/channel-setup`:
+ const setupSurface = createOptionalChannelSetupSurface({
+ channel: "my-channel",
+ label: "My Channel",
+ npmSpec: "@myorg/openclaw-my-channel",
+ docsPath: "/channels/my-channel",
+ });
+ // Returns { setupAdapter, setupWizard }
+ ```
-```typescript
-import { createOptionalChannelSetupSurface } from "openclaw/plugin-sdk/channel-setup";
+ `plugin-sdk/channel-setup` also exposes the lower-level `createOptionalChannelSetupAdapter(...)` and `createOptionalChannelSetupWizard(...)` builders when you only need one half of that optional-install surface.
-const setupSurface = createOptionalChannelSetupSurface({
- channel: "my-channel",
- label: "My Channel",
- npmSpec: "@myorg/openclaw-my-channel",
- docsPath: "/channels/my-channel",
-});
-// Returns { setupAdapter, setupWizard }
-```
+ The generated optional adapter/wizard fail closed on real config writes. They reuse one install-required message across `validateInput`, `applyAccountConfig`, and `finalize`, and append a docs link when `docsPath` is set.
-`plugin-sdk/channel-setup` also exposes the lower-level
-`createOptionalChannelSetupAdapter(...)` and
-`createOptionalChannelSetupWizard(...)` builders when you only need one half of
-that optional-install surface.
+
+
+ For binary-backed setup UIs, prefer the shared delegated helpers instead of copying the same binary/status glue into every channel:
-The generated optional adapter/wizard fail closed on real config writes. They
-reuse one install-required message across `validateInput`,
-`applyAccountConfig`, and `finalize`, and append a docs link when `docsPath` is
-set.
+ - `createDetectedBinaryStatus(...)` for status blocks that vary only by labels, hints, scores, and binary detection
+ - `createCliPathTextInput(...)` for path-backed text inputs
+ - `createDelegatedSetupWizardStatusResolvers(...)`, `createDelegatedPrepare(...)`, `createDelegatedFinalize(...)`, and `createDelegatedResolveConfigured(...)` when `setupEntry` needs to forward to a heavier full wizard lazily
+ - `createDelegatedTextInputShouldPrompt(...)` when `setupEntry` only needs to delegate a `textInputs[*].shouldPrompt` decision
-For binary-backed setup UIs, prefer the shared delegated helpers instead of
-copying the same binary/status glue into every channel:
-
-- `createDetectedBinaryStatus(...)` for status blocks that vary only by labels,
- hints, scores, and binary detection
-- `createCliPathTextInput(...)` for path-backed text inputs
-- `createDelegatedSetupWizardStatusResolvers(...)`,
- `createDelegatedPrepare(...)`, `createDelegatedFinalize(...)`, and
- `createDelegatedResolveConfigured(...)` when `setupEntry` needs to forward to
- a heavier full wizard lazily
-- `createDelegatedTextInputShouldPrompt(...)` when `setupEntry` only needs to
- delegate a `textInputs[*].shouldPrompt` decision
+
+
## Publishing and installing
**External plugins:** publish to [ClawHub](/tools/clawhub) or npm, then install:
-```bash
-openclaw plugins install @myorg/openclaw-my-plugin
-```
+
+
+ ```bash
+ openclaw plugins install @myorg/openclaw-my-plugin
+ ```
-OpenClaw tries ClawHub first and falls back to npm automatically. You can also
-force ClawHub explicitly:
+ OpenClaw tries ClawHub first and falls back to npm automatically.
-```bash
-openclaw plugins install clawhub:@myorg/openclaw-my-plugin # ClawHub only
-```
+
+
+ ```bash
+ openclaw plugins install clawhub:@myorg/openclaw-my-plugin
+ ```
+
+
+ There is no matching `npm:` override. Use the normal npm package spec when you want the npm path after ClawHub fallback:
-There is no matching `npm:` override. Use the normal npm package spec when you
-want the npm path after ClawHub fallback:
+ ```bash
+ openclaw plugins install @myorg/openclaw-my-plugin
+ ```
-```bash
-openclaw plugins install @myorg/openclaw-my-plugin
-```
+
+
-**In-repo plugins:** place under the bundled plugin workspace tree and they are automatically
-discovered during build.
+**In-repo plugins:** place under the bundled plugin workspace tree and they are automatically discovered during build.
**Users can install:**
@@ -553,20 +510,15 @@ openclaw plugins install
```
- For npm-sourced installs, `openclaw plugins install` runs
- project-local `npm install --ignore-scripts` (no lifecycle scripts), ignoring
- inherited global npm install settings. Keep plugin dependency trees pure JS/TS
- and avoid packages that require `postinstall` builds.
+For npm-sourced installs, `openclaw plugins install` runs project-local `npm install --ignore-scripts` (no lifecycle scripts), ignoring inherited global npm install settings. Keep plugin dependency trees pure JS/TS and avoid packages that require `postinstall` builds.
-Bundled OpenClaw-owned plugins are the only startup repair exception: when a
-packaged install sees one enabled by plugin config, legacy channel config, or
-its bundled default-enabled manifest, startup installs that plugin's missing
-runtime dependencies before import. Third-party plugins should not rely on
-startup installs; keep using the explicit plugin installer.
+
+Bundled OpenClaw-owned plugins are the only startup repair exception: when a packaged install sees one enabled by plugin config, legacy channel config, or its bundled default-enabled manifest, startup installs that plugin's missing runtime dependencies before import. Third-party plugins should not rely on startup installs; keep using the explicit plugin installer.
+
## Related
-- [SDK entry points](/plugins/sdk-entrypoints) — `definePluginEntry` and `defineChannelPluginEntry`
-- [Plugin manifest](/plugins/manifest) — full manifest schema reference
- [Building plugins](/plugins/building-plugins) — step-by-step getting started guide
+- [Plugin manifest](/plugins/manifest) — full manifest schema reference
+- [SDK entry points](/plugins/sdk-entrypoints) — `definePluginEntry` and `defineChannelPluginEntry`