mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-07 09:17:12 +08:00
Source-grounded rewrite of 529 published docs pages with per-unit information-loss verification: 1,713 factual corrections cited to src/**, generated surfaces regenerated, frontmatter titles preserved for i18n, release notes pages untouched. All docs gates green. Closes #100141
560 lines
27 KiB
Markdown
560 lines
27 KiB
Markdown
---
|
|
summary: "Setup wizards, setup-entry.ts, config schemas, and package.json metadata"
|
|
title: "Plugin 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.
|
|
|
|
<Tip>
|
|
**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).
|
|
</Tip>
|
|
|
|
## Package metadata
|
|
|
|
Your `package.json` needs an `openclaw` field that tells the plugin system what your plugin provides:
|
|
|
|
<Tabs>
|
|
<Tab title="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."
|
|
}
|
|
}
|
|
}
|
|
```
|
|
</Tab>
|
|
<Tab title="Provider plugin / ClawHub baseline">
|
|
```json openclaw-clawhub-package.json
|
|
{
|
|
"name": "@myorg/openclaw-my-plugin",
|
|
"version": "1.0.0",
|
|
"type": "module",
|
|
"dependencies": {
|
|
"typebox": "1.1.39"
|
|
},
|
|
"peerDependencies": {
|
|
"openclaw": ">=2026.3.24-beta.2"
|
|
},
|
|
"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"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
</Tab>
|
|
</Tabs>
|
|
|
|
<Note>
|
|
Publishing externally on ClawHub requires `compat` and `build`. Canonical publish snippets live in `docs/snippets/plugin-publish/`.
|
|
</Note>
|
|
|
|
### `openclaw` fields
|
|
|
|
<ParamField path="extensions" type="string[]">
|
|
Entry point files (relative to package root). Valid source entries for workspace and git checkout development.
|
|
</ParamField>
|
|
<ParamField path="runtimeExtensions" type="string[]">
|
|
Built JavaScript peers for `extensions`, preferred when OpenClaw loads an installed npm package. See [SDK entry points](/plugins/sdk-entrypoints) for the source/built resolution order.
|
|
</ParamField>
|
|
<ParamField path="setupEntry" type="string">
|
|
Lightweight setup-only entry (optional).
|
|
</ParamField>
|
|
<ParamField path="runtimeSetupEntry" type="string">
|
|
Built JavaScript peer for `setupEntry`. Requires `setupEntry` to also be set.
|
|
</ParamField>
|
|
<ParamField path="plugin" type="object">
|
|
`{ id, label }` fallback plugin identity, used when a plugin has no channel/provider metadata to derive an id or label from.
|
|
</ParamField>
|
|
<ParamField path="channel" type="object">
|
|
Channel catalog metadata for setup, picker, quickstart, and status surfaces.
|
|
</ParamField>
|
|
<ParamField path="install" type="object">
|
|
Install hints: `npmSpec`, `localPath`, `defaultChoice`, `minHostVersion`, `expectedIntegrity`, `allowInvalidConfigRecovery`, `requiredPlatformPackages`.
|
|
</ParamField>
|
|
<ParamField path="startup" type="object">
|
|
Startup behavior flags.
|
|
</ParamField>
|
|
<ParamField path="compat" type="object">
|
|
`pluginApi` version range this plugin supports. Required for external ClawHub publishes.
|
|
</ParamField>
|
|
|
|
<Note>
|
|
Provider ids (`providers: string[]`) are manifest metadata, not package metadata. Declare them in `openclaw.plugin.json`, not here — see [Plugin manifest](/plugins/manifest).
|
|
</Note>
|
|
|
|
### `openclaw.channel`
|
|
|
|
`openclaw.channel` is cheap package metadata for channel discovery and setup surfaces before runtime loads.
|
|
|
|
| Field | Type | What it means |
|
|
| -------------------------------------- | ---------- | ----------------------------------------------------------------------------- |
|
|
| `id` | `string` | Canonical channel id. |
|
|
| `label` | `string` | Primary channel label. |
|
|
| `selectionLabel` | `string` | Picker/setup label when it should differ from `label`. |
|
|
| `detailLabel` | `string` | Secondary detail label for richer channel catalogs and status surfaces. |
|
|
| `docsPath` | `string` | Docs path for setup and selection links. |
|
|
| `docsLabel` | `string` | Override label used for docs links when it should differ from the channel id. |
|
|
| `blurb` | `string` | Short onboarding/catalog description. |
|
|
| `order` | `number` | Sort order in channel catalogs. |
|
|
| `aliases` | `string[]` | Extra lookup aliases for channel selection. |
|
|
| `preferOver` | `string[]` | Lower-priority plugin/channel ids this channel should outrank. |
|
|
| `systemImage` | `string` | Optional icon/system-image name for channel UI catalogs. |
|
|
| `selectionDocsPrefix` | `string` | Prefix text before docs links in selection surfaces. |
|
|
| `selectionDocsOmitLabel` | `boolean` | Show the docs path directly instead of a labeled docs link in selection copy. |
|
|
| `selectionExtras` | `string[]` | Extra short strings appended in selection copy. |
|
|
| `markdownCapable` | `boolean` | Marks the channel as markdown-capable for outbound formatting decisions. |
|
|
| `exposure` | `object` | Channel visibility controls for setup, configured lists, and docs surfaces. |
|
|
| `quickstartAllowFrom` | `boolean` | Opt this channel into the standard quickstart `allowFrom` setup flow. |
|
|
| `forceAccountBinding` | `boolean` | Require explicit account binding even when only one account exists. |
|
|
| `preferSessionLookupForAnnounceTarget` | `boolean` | Prefer session lookup when resolving announce targets for this channel. |
|
|
|
|
Example:
|
|
|
|
```json
|
|
{
|
|
"openclaw": {
|
|
"channel": {
|
|
"id": "my-channel",
|
|
"label": "My Channel",
|
|
"selectionLabel": "My Channel (self-hosted)",
|
|
"detailLabel": "My Channel Bot",
|
|
"docsPath": "/channels/my-channel",
|
|
"docsLabel": "my-channel",
|
|
"blurb": "Webhook-based self-hosted chat integration.",
|
|
"order": 80,
|
|
"aliases": ["mc"],
|
|
"preferOver": ["my-channel-legacy"],
|
|
"selectionDocsPrefix": "Guide:",
|
|
"selectionExtras": ["Markdown"],
|
|
"markdownCapable": true,
|
|
"exposure": {
|
|
"configured": true,
|
|
"setup": true,
|
|
"docs": true
|
|
},
|
|
"quickstartAllowFrom": true
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
`exposure` supports:
|
|
|
|
- `configured`: include the channel in configured/status-style listing surfaces
|
|
- `setup`: include the channel in interactive setup/configure pickers
|
|
- `docs`: mark the channel as public-facing in docs/navigation surfaces
|
|
|
|
<Note>
|
|
`showConfigured` and `showInSetup` remain supported as legacy aliases. Prefer `exposure`.
|
|
</Note>
|
|
|
|
### `openclaw.install`
|
|
|
|
`openclaw.install` is package metadata, not manifest metadata.
|
|
|
|
| Field | Type | What it means |
|
|
| ---------------------------- | ----------------------------------- | --------------------------------------------------------------------------------- |
|
|
| `clawhubSpec` | `string` | Canonical ClawHub spec for install/update and onboarding install-on-demand flows. |
|
|
| `npmSpec` | `string` | Canonical npm spec for install/update fallback flows. |
|
|
| `localPath` | `string` | Local development or bundled install path. |
|
|
| `defaultChoice` | `"clawhub"` \| `"npm"` \| `"local"` | Preferred install source when multiple sources are available. |
|
|
| `minHostVersion` | `string` | Minimum supported OpenClaw version, `>=x.y.z` or `>=x.y.z-prerelease`. |
|
|
| `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. |
|
|
| `requiredPlatformPackages` | `string[]` | Required platform-specific npm aliases verified during npm install. |
|
|
|
|
<AccordionGroup>
|
|
<Accordion title="Onboarding behavior">
|
|
Interactive onboarding 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 prompt for ClawHub, npm, or local install, install or enable the plugin, then continue the selected flow. ClawHub choices use `clawhubSpec` and are preferred when present; npm choices require trusted catalog metadata with a registry `npmSpec` (exact versions and `expectedIntegrity` are optional pins, enforced on install/update when set). Keep "what to show" in `openclaw.plugin.json` and "how to install it" in `package.json`.
|
|
</Accordion>
|
|
<Accordion title="minHostVersion enforcement">
|
|
If `minHostVersion` is set, install and non-bundled manifest-registry loading both enforce it. Older hosts skip external plugins; invalid version strings are rejected. Bundled source plugins are assumed to be co-versioned with the host checkout.
|
|
</Accordion>
|
|
<Accordion title="Pinned npm installs">
|
|
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"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
</Accordion>
|
|
<Accordion title="allowInvalidConfigRecovery scope">
|
|
`allowInvalidConfigRecovery` is not a general bypass for broken configs. It is narrow bundled-plugin recovery only, letting reinstall/setup repair known upgrade leftovers like a missing bundled plugin path or a stale `channels.<id>` 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`.
|
|
</Accordion>
|
|
</AccordionGroup>
|
|
|
|
### Deferred full load
|
|
|
|
Channel plugins can opt into deferred loading with:
|
|
|
|
```json
|
|
{
|
|
"openclaw": {
|
|
"extensions": ["./index.ts"],
|
|
"setupEntry": "./setup-entry.ts",
|
|
"startup": {
|
|
"deferConfiguredChannelFullLoadUntilAfterListen": true
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
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.
|
|
|
|
<Warning>
|
|
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.
|
|
</Warning>
|
|
|
|
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 normalize 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.
|
|
|
|
```json
|
|
{
|
|
"id": "my-plugin",
|
|
"name": "My Plugin",
|
|
"description": "Adds My Plugin capabilities to OpenClaw",
|
|
"configSchema": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"webhookSecret": {
|
|
"type": "string",
|
|
"description": "Webhook verification secret"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
For channel plugins, add `channels` (and provider plugins add `providers`):
|
|
|
|
```json
|
|
{
|
|
"id": "my-channel",
|
|
"channels": ["my-channel"],
|
|
"configSchema": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"properties": {}
|
|
}
|
|
}
|
|
```
|
|
|
|
Even plugins with no config must ship a schema. An empty schema is valid:
|
|
|
|
```json
|
|
{
|
|
"id": "my-plugin",
|
|
"configSchema": {
|
|
"type": "object",
|
|
"additionalProperties": false
|
|
}
|
|
}
|
|
```
|
|
|
|
See [Plugin manifest](/plugins/manifest) for the full schema reference.
|
|
|
|
## ClawHub publishing
|
|
|
|
Skills and plugin packages use separate ClawHub publish commands. For plugin packages, use the package-specific command:
|
|
|
|
```bash
|
|
clawhub package publish your-org/your-plugin --dry-run
|
|
clawhub package publish your-org/your-plugin
|
|
```
|
|
|
|
<Note>
|
|
`clawhub skill publish <path>` is a different command for publishing a skill folder, not a plugin package. See [Publishing on ClawHub](/clawhub/publishing).
|
|
</Note>
|
|
|
|
## Setup entry
|
|
|
|
`setup-entry.ts` 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
|
|
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";
|
|
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.
|
|
|
|
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.
|
|
|
|
<AccordionGroup>
|
|
<Accordion title="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`).
|
|
|
|
</Accordion>
|
|
<Accordion title="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.*`.
|
|
|
|
</Accordion>
|
|
<Accordion title="What setupEntry should NOT include">
|
|
- CLI registrations.
|
|
- Background services.
|
|
- Heavy runtime imports (crypto, SDKs).
|
|
- Gateway methods only needed after startup.
|
|
|
|
</Accordion>
|
|
</AccordionGroup>
|
|
|
|
### 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:
|
|
|
|
| Import path | Use it for | Key exports |
|
|
| ---------------------------------- | ----------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
| `plugin-sdk/setup-runtime` | setup-time runtime helpers that stay available in `setupEntry` / deferred channel startup | `createSetupTranslator`, `createPatchedAccountSetupAdapter`, `createEnvPatchedAccountSetupAdapter`, `createSetupInputPresenceValidator`, `noteChannelLookupFailure`, `noteChannelLookupSummary`, `promptResolvedAllowFrom`, `splitSetupEntries`, `createAllowlistSetupWizardProxy`, `createDelegatedSetupWizardProxy` |
|
|
| `plugin-sdk/setup-adapter-runtime` | deprecated compatibility alias; use `plugin-sdk/setup-runtime` | `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 `createSetupTranslator(...)` for fixed setup wizard copy. It follows the CLI wizard locale (`OPENCLAW_LOCALE`, then system locale variables) and falls back to English. Keep plugin-specific setup text in plugin-owned code and use shared catalog keys only for common setup labels, status text, and official bundled plugin setup copy.
|
|
|
|
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.<id>.accounts.*`, the default shared behavior moves promoted account-scoped values into `accounts.default`.
|
|
|
|
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
|
|
|
|
<Note>
|
|
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.
|
|
</Note>
|
|
|
|
## Config schema
|
|
|
|
Plugin config is validated against the JSON Schema in your manifest. Users configure plugins via:
|
|
|
|
```json5
|
|
{
|
|
plugins: {
|
|
entries: {
|
|
"my-plugin": {
|
|
config: {
|
|
webhookSecret: "abc123",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
```
|
|
|
|
Your plugin receives this config as `api.pluginConfig` during registration.
|
|
|
|
For channel-specific config, use the channel config section instead:
|
|
|
|
```json5
|
|
{
|
|
channels: {
|
|
"my-channel": {
|
|
token: "bot-token",
|
|
allowFrom: ["user1", "user2"],
|
|
},
|
|
},
|
|
}
|
|
```
|
|
|
|
### Building channel config schemas
|
|
|
|
Use `buildChannelConfigSchema` to convert a Zod schema into the `ChannelConfigSchema` wrapper used by plugin-owned config artifacts:
|
|
|
|
```typescript
|
|
import { z } from "zod";
|
|
import { buildChannelConfigSchema } from "openclaw/plugin-sdk/channel-config-schema";
|
|
|
|
const accountSchema = z.object({
|
|
token: z.string().optional(),
|
|
allowFrom: z.array(z.string()).optional(),
|
|
accounts: z.object({}).catchall(z.any()).optional(),
|
|
defaultAccount: z.string().optional(),
|
|
});
|
|
|
|
const configSchema = buildChannelConfigSchema(accountSchema);
|
|
```
|
|
|
|
If you already author the contract as JSON Schema or TypeBox, use the direct helper so OpenClaw can skip Zod-to-JSON-Schema conversion on metadata paths:
|
|
|
|
```typescript
|
|
import { Type } from "typebox";
|
|
import { buildJsonChannelConfigSchema } from "openclaw/plugin-sdk/channel-config-schema";
|
|
|
|
const configSchema = buildJsonChannelConfigSchema(
|
|
Type.Object({
|
|
token: Type.Optional(Type.String()),
|
|
allowFrom: Type.Optional(Type.Array(Type.String())),
|
|
}),
|
|
);
|
|
```
|
|
|
|
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.<id>` 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`:
|
|
|
|
```typescript
|
|
import type { ChannelSetupWizard } from "openclaw/plugin-sdk/channel-setup";
|
|
|
|
const setupWizard: ChannelSetupWizard = {
|
|
channel: "my-channel",
|
|
status: {
|
|
configuredLabel: "Connected",
|
|
unconfiguredLabel: "Not configured",
|
|
resolveConfigured: ({ cfg }) => Boolean((cfg.channels as any)?.["my-channel"]?.token),
|
|
},
|
|
credentials: [
|
|
{
|
|
inputKey: "token",
|
|
providerHint: "my-channel",
|
|
credentialLabel: "Bot token",
|
|
preferredEnvVar: "MY_CHANNEL_BOT_TOKEN",
|
|
envPrompt: "Use MY_CHANNEL_BOT_TOKEN from environment?",
|
|
keepPrompt: "Keep current token?",
|
|
inputPrompt: "Enter your bot token:",
|
|
inspect: ({ cfg, accountId }) => {
|
|
const token = (cfg.channels as any)?.["my-channel"]?.token;
|
|
return {
|
|
accountConfigured: Boolean(token),
|
|
hasConfiguredValue: Boolean(token),
|
|
};
|
|
},
|
|
},
|
|
],
|
|
};
|
|
```
|
|
|
|
`ChannelSetupWizard` also supports `textInputs`, `dmPolicy`, `allowFrom`, `groupAccess`, `prepare`, `finalize`, and more. See the Discord plugin's `src/setup-core.ts` for a full bundled example.
|
|
|
|
<AccordionGroup>
|
|
<Accordion title="Shared allowFrom prompts">
|
|
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(...)`.
|
|
</Accordion>
|
|
<Accordion title="Standard channel setup status">
|
|
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.
|
|
</Accordion>
|
|
<Accordion title="Optional channel setup surface">
|
|
For optional setup surfaces that should only appear in certain contexts, use `createOptionalChannelSetupSurface` from `openclaw/plugin-sdk/channel-setup`:
|
|
|
|
```typescript
|
|
import { 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 }
|
|
```
|
|
|
|
`plugin-sdk/channel-setup` also exposes the lower-level `createOptionalChannelSetupAdapter(...)` and `createOptionalChannelSetupWizard(...)` builders when you only need one half of that optional-install surface.
|
|
|
|
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.
|
|
|
|
</Accordion>
|
|
<Accordion title="Binary-backed setup helpers">
|
|
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
|
|
|
|
</Accordion>
|
|
</AccordionGroup>
|
|
|
|
## Publishing and installing
|
|
|
|
**External plugins:** publish to [ClawHub](/clawhub), then install:
|
|
|
|
<Tabs>
|
|
<Tab title="npm">
|
|
```bash
|
|
openclaw plugins install @myorg/openclaw-my-plugin
|
|
```
|
|
|
|
Bare package specs install from npm during the launch cutover, unless the name matches a bundled or official plugin id, in which case OpenClaw uses that local/official copy instead. Use `clawhub:`, `npm:`, `git:`, or `npm-pack:` for deterministic source selection — see [Manage plugins](/plugins/manage-plugins).
|
|
|
|
</Tab>
|
|
<Tab title="ClawHub only">
|
|
```bash
|
|
openclaw plugins install clawhub:@myorg/openclaw-my-plugin
|
|
```
|
|
</Tab>
|
|
<Tab title="npm package spec">
|
|
Use npm when a package has not moved to ClawHub yet, or when you need a
|
|
direct npm install path during migration:
|
|
|
|
```bash
|
|
openclaw plugins install npm:@myorg/openclaw-my-plugin
|
|
```
|
|
|
|
</Tab>
|
|
</Tabs>
|
|
|
|
**In-repo plugins:** place under the bundled plugin workspace tree; they are automatically discovered during build.
|
|
|
|
<Info>
|
|
For npm-sourced installs, `openclaw plugins install` installs the package into a per-plugin project under `~/.openclaw/npm/projects` with lifecycle scripts disabled (`--ignore-scripts`). Keep plugin dependency trees pure JS/TS and avoid packages that require `postinstall` builds.
|
|
</Info>
|
|
|
|
<Note>
|
|
Gateway startup does not install plugin dependencies. npm/git/ClawHub install flows own dependency convergence; local plugins must already have their dependencies installed.
|
|
</Note>
|
|
|
|
Bundled package metadata is explicit, not inferred from built JavaScript at gateway startup. Runtime dependencies belong in the plugin package that owns them; packaged OpenClaw startup never repairs or mirrors plugin dependencies.
|
|
|
|
## Related
|
|
|
|
- [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`
|