mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-27 01:41:40 +00:00
docs(plugin-sdk): document public SDK surface
This commit is contained in:
@@ -238,5 +238,65 @@
|
||||
{
|
||||
"source": "env var",
|
||||
"target": "环境变量"
|
||||
},
|
||||
{
|
||||
"source": "Plugin SDK",
|
||||
"target": "插件 SDK"
|
||||
},
|
||||
{
|
||||
"source": "Plugin SDK Overview",
|
||||
"target": "插件 SDK 概览"
|
||||
},
|
||||
{
|
||||
"source": "SDK Overview",
|
||||
"target": "SDK 概览"
|
||||
},
|
||||
{
|
||||
"source": "Plugin Entry Points",
|
||||
"target": "插件入口点"
|
||||
},
|
||||
{
|
||||
"source": "Entry Points",
|
||||
"target": "入口点"
|
||||
},
|
||||
{
|
||||
"source": "Plugin Runtime",
|
||||
"target": "插件运行时"
|
||||
},
|
||||
{
|
||||
"source": "Runtime",
|
||||
"target": "运行时"
|
||||
},
|
||||
{
|
||||
"source": "Plugin Setup",
|
||||
"target": "插件设置"
|
||||
},
|
||||
{
|
||||
"source": "Setup",
|
||||
"target": "设置"
|
||||
},
|
||||
{
|
||||
"source": "Channel Plugin SDK",
|
||||
"target": "渠道插件 SDK"
|
||||
},
|
||||
{
|
||||
"source": "Channel Plugins",
|
||||
"target": "渠道插件"
|
||||
},
|
||||
{
|
||||
"source": "Provider Plugin SDK",
|
||||
"target": "提供商插件 SDK"
|
||||
},
|
||||
{
|
||||
"source": "Provider Plugins",
|
||||
"target": "提供商插件"
|
||||
},
|
||||
{
|
||||
"source": "Plugin SDK Testing",
|
||||
"target": "插件 SDK 测试"
|
||||
},
|
||||
{
|
||||
"source": "Testing",
|
||||
"target": "测试"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1045,6 +1045,18 @@
|
||||
"plugins/architecture"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Plugin SDK",
|
||||
"pages": [
|
||||
"plugins/sdk-overview",
|
||||
"plugins/sdk-entrypoints",
|
||||
"plugins/sdk-runtime",
|
||||
"plugins/sdk-setup",
|
||||
"plugins/sdk-channel-plugins",
|
||||
"plugins/sdk-provider-plugins",
|
||||
"plugins/sdk-testing"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Skills",
|
||||
"pages": [
|
||||
|
||||
@@ -181,7 +181,7 @@ my-plugin/
|
||||
// Correct: focused subpaths
|
||||
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
||||
import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
|
||||
import { buildOauthProviderAuthResult } from "openclaw/plugin-sdk/provider-oauth";
|
||||
import { buildOauthProviderAuthResult } from "openclaw/plugin-sdk/provider-auth";
|
||||
|
||||
// Wrong: monolithic root (lint will reject this)
|
||||
import { ... } from "openclaw/plugin-sdk";
|
||||
@@ -195,22 +195,23 @@ my-plugin/
|
||||
| --- | --- |
|
||||
| `plugin-sdk/plugin-entry` | Canonical `definePluginEntry` helper + provider/plugin entry types |
|
||||
| `plugin-sdk/core` | Channel entry helpers, channel builders, and shared base types |
|
||||
| `plugin-sdk/channel-setup` | Setup wizard adapters |
|
||||
| `plugin-sdk/runtime-store` | Safe module-level runtime storage |
|
||||
| `plugin-sdk/setup` | Shared setup-wizard helpers |
|
||||
| `plugin-sdk/channel-setup` | Channel setup adapters |
|
||||
| `plugin-sdk/channel-pairing` | DM pairing primitives |
|
||||
| `plugin-sdk/channel-reply-pipeline` | Reply prefix + typing wiring |
|
||||
| `plugin-sdk/channel-config-schema` | Config schema builders |
|
||||
| `plugin-sdk/channel-policy` | Group/DM policy helpers |
|
||||
| `plugin-sdk/channel-actions` | Shared `message` tool schema helpers |
|
||||
| `plugin-sdk/channel-contract` | Pure channel types |
|
||||
| `plugin-sdk/secret-input` | Secret input parsing/helpers |
|
||||
| `plugin-sdk/webhook-ingress` | Webhook request/target helpers |
|
||||
| `plugin-sdk/runtime-store` | Persistent plugin storage |
|
||||
| `plugin-sdk/allow-from` | Allowlist resolution |
|
||||
| `plugin-sdk/reply-payload` | Message reply types |
|
||||
| `plugin-sdk/provider-oauth` | OAuth login + PKCE helpers |
|
||||
| `plugin-sdk/provider-auth` | Provider auth and OAuth helpers |
|
||||
| `plugin-sdk/provider-onboard` | Provider onboarding config patches |
|
||||
| `plugin-sdk/provider-models` | Model catalog helpers |
|
||||
| `plugin-sdk/testing` | Test utilities |
|
||||
</Accordion>
|
||||
|
||||
Use the narrowest subpath that matches the job.
|
||||
Use the narrowest subpath that matches the job. For the curated map and
|
||||
examples, see [Plugin SDK Overview](/plugins/sdk-overview).
|
||||
|
||||
</Step>
|
||||
|
||||
@@ -266,7 +267,7 @@ my-plugin/
|
||||
For unit tests, import test helpers from the testing surface:
|
||||
|
||||
```typescript
|
||||
import { createTestRuntime } from "openclaw/plugin-sdk/testing";
|
||||
import { createWindowsCmdShimFixture } from "openclaw/plugin-sdk/testing";
|
||||
```
|
||||
|
||||
</Step>
|
||||
@@ -370,6 +371,13 @@ patterns is strongly recommended.
|
||||
## Related
|
||||
|
||||
- [Plugin SDK Migration](/plugins/sdk-migration) — migrating from deprecated compat surfaces
|
||||
- [Plugin SDK Overview](/plugins/sdk-overview) — public SDK map and subpath guidance
|
||||
- [Plugin Entry Points](/plugins/sdk-entrypoints) — `definePluginEntry` and `defineChannelPluginEntry`
|
||||
- [Plugin Runtime](/plugins/sdk-runtime) — injected runtime and runtime-store
|
||||
- [Plugin Setup](/plugins/sdk-setup) — setup, channel setup, and secret input helpers
|
||||
- [Channel Plugin SDK](/plugins/sdk-channel-plugins) — channel contracts and actions
|
||||
- [Provider Plugin SDK](/plugins/sdk-provider-plugins) — provider auth, onboarding, and catalogs
|
||||
- [Plugin SDK Testing](/plugins/sdk-testing) — public test helpers
|
||||
- [Plugin Architecture](/plugins/architecture) — internals and capability model
|
||||
- [Plugin Manifest](/plugins/manifest) — full manifest schema
|
||||
- [Plugin Agent Tools](/plugins/building-plugins#registering-agent-tools) — adding agent tools in a plugin
|
||||
|
||||
161
docs/plugins/sdk-channel-plugins.md
Normal file
161
docs/plugins/sdk-channel-plugins.md
Normal file
@@ -0,0 +1,161 @@
|
||||
---
|
||||
title: "Channel Plugin SDK"
|
||||
sidebarTitle: "Channel Plugins"
|
||||
summary: "Contracts and helpers for native messaging channel plugins, including actions, routing, pairing, and setup"
|
||||
read_when:
|
||||
- You are building a native channel plugin
|
||||
- You need to implement the shared `message` tool for a channel
|
||||
- You need pairing, setup, or routing helpers for a channel
|
||||
---
|
||||
|
||||
# Channel Plugin SDK
|
||||
|
||||
Channel plugins use `defineChannelPluginEntry(...)` from
|
||||
`openclaw/plugin-sdk/core` and implement the `ChannelPlugin` contract.
|
||||
|
||||
## Minimal channel entry
|
||||
|
||||
```ts
|
||||
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
|
||||
import { exampleChannelPlugin } from "./src/channel.js";
|
||||
import { setExampleRuntime } from "./src/runtime.js";
|
||||
|
||||
export default defineChannelPluginEntry({
|
||||
id: "example-channel",
|
||||
name: "Example Channel",
|
||||
description: "Example native channel plugin",
|
||||
plugin: exampleChannelPlugin,
|
||||
setRuntime: setExampleRuntime,
|
||||
});
|
||||
```
|
||||
|
||||
## `ChannelPlugin` shape
|
||||
|
||||
Important sections of the contract:
|
||||
|
||||
- `meta`: docs, labels, and picker metadata
|
||||
- `capabilities`: replies, polls, reactions, threads, media, and chat types
|
||||
- `config` and `configSchema`: account resolution and config parsing
|
||||
- `setup` and `setupWizard`: onboarding/setup flow
|
||||
- `security`: DM policy and allowlist behavior
|
||||
- `messaging`: target parsing and outbound session routing
|
||||
- `actions`: shared `message` tool discovery and execution
|
||||
- `pairing`, `threading`, `status`, `lifecycle`, `groups`, `directory`
|
||||
|
||||
For pure types, import from `openclaw/plugin-sdk/channel-contract`.
|
||||
|
||||
## Shared `message` tool
|
||||
|
||||
Channel plugins own their channel-specific part of the shared `message` tool
|
||||
through `ChannelMessageActionAdapter`.
|
||||
|
||||
```ts
|
||||
import { Type } from "@sinclair/typebox";
|
||||
import { createMessageToolButtonsSchema } from "openclaw/plugin-sdk/channel-actions";
|
||||
|
||||
export const exampleActions = {
|
||||
describeMessageTool() {
|
||||
return {
|
||||
actions: ["send", "edit"],
|
||||
capabilities: ["buttons"],
|
||||
schema: {
|
||||
visibility: "current-channel",
|
||||
properties: {
|
||||
buttons: createMessageToolButtonsSchema(),
|
||||
threadId: Type.String(),
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
async handleAction(ctx) {
|
||||
if (ctx.action === "send") {
|
||||
return {
|
||||
content: [{ type: "text", text: `send to ${String(ctx.params.to)}` }],
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
content: [{ type: "text", text: `unsupported action: ${ctx.action}` }],
|
||||
};
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
Key types:
|
||||
|
||||
- `ChannelMessageActionAdapter`
|
||||
- `ChannelMessageActionContext`
|
||||
- `ChannelMessageActionDiscoveryContext`
|
||||
- `ChannelMessageToolDiscovery`
|
||||
|
||||
## Outbound routing helpers
|
||||
|
||||
When a channel plugin needs custom outbound routing, implement
|
||||
`messaging.resolveOutboundSessionRoute(...)`.
|
||||
|
||||
Use `buildChannelOutboundSessionRoute(...)` from `plugin-sdk/core` to return the
|
||||
standard route payload:
|
||||
|
||||
```ts
|
||||
import { buildChannelOutboundSessionRoute } from "openclaw/plugin-sdk/core";
|
||||
|
||||
const messaging = {
|
||||
resolveOutboundSessionRoute({ cfg, agentId, accountId, target }) {
|
||||
return buildChannelOutboundSessionRoute({
|
||||
cfg,
|
||||
agentId,
|
||||
channel: "example-channel",
|
||||
accountId,
|
||||
peer: { kind: "direct", id: target },
|
||||
chatType: "direct",
|
||||
from: accountId ?? "default",
|
||||
to: target,
|
||||
});
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
## Pairing helpers
|
||||
|
||||
Use `plugin-sdk/channel-pairing` for DM approval flows:
|
||||
|
||||
```ts
|
||||
import { createChannelPairingController } from "openclaw/plugin-sdk/channel-pairing";
|
||||
|
||||
const pairing = createChannelPairingController({
|
||||
core: runtime,
|
||||
channel: "example-channel",
|
||||
accountId: "default",
|
||||
});
|
||||
|
||||
const result = pairing.issueChallenge({
|
||||
agentId: "assistant",
|
||||
requesterId: "user-123",
|
||||
});
|
||||
```
|
||||
|
||||
That surface also gives you scoped access to pairing storage helpers such as
|
||||
allowlist reads and request upserts.
|
||||
|
||||
## Channel setup helpers
|
||||
|
||||
Use:
|
||||
|
||||
- `plugin-sdk/channel-setup` for optional or installable channels
|
||||
- `plugin-sdk/setup` for setup adapters, DM policy, and allowlist prompts
|
||||
- `plugin-sdk/webhook-ingress` for plugin-owned webhook routes
|
||||
|
||||
## Channel plugin guidance
|
||||
|
||||
- Keep transport-specific execution inside the channel package.
|
||||
- Use `channel-contract` types in tests and local helpers.
|
||||
- Keep `describeMessageTool(...)` and `handleAction(...)` aligned.
|
||||
- Keep session routing in `messaging`, not in ad-hoc command handlers.
|
||||
- Prefer focused subpaths over broad runtime coupling.
|
||||
|
||||
## Related
|
||||
|
||||
- [Plugin SDK Overview](/plugins/sdk-overview)
|
||||
- [Plugin Entry Points](/plugins/sdk-entrypoints)
|
||||
- [Plugin Setup](/plugins/sdk-setup)
|
||||
- [Plugin Internals](/plugins/architecture)
|
||||
159
docs/plugins/sdk-entrypoints.md
Normal file
159
docs/plugins/sdk-entrypoints.md
Normal file
@@ -0,0 +1,159 @@
|
||||
---
|
||||
title: "Plugin Entry Points"
|
||||
sidebarTitle: "Entry Points"
|
||||
summary: "How to define plugin entry files for provider, tool, channel, and setup plugins"
|
||||
read_when:
|
||||
- You are writing a plugin `index.ts`
|
||||
- You need to choose between `definePluginEntry` and `defineChannelPluginEntry`
|
||||
- You are adding a separate `setup-entry.ts`
|
||||
---
|
||||
|
||||
# Plugin Entry Points
|
||||
|
||||
OpenClaw has two main entry helpers:
|
||||
|
||||
- `definePluginEntry(...)` for general plugins
|
||||
- `defineChannelPluginEntry(...)` for native messaging channels
|
||||
|
||||
There is also `defineSetupPluginEntry(...)` for a separate setup-only module.
|
||||
|
||||
## `definePluginEntry(...)`
|
||||
|
||||
Use this for providers, tools, commands, services, memory plugins, and context
|
||||
engines.
|
||||
|
||||
```ts
|
||||
import { definePluginEntry, type OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-entry";
|
||||
|
||||
export default definePluginEntry({
|
||||
id: "example-tools",
|
||||
name: "Example Tools",
|
||||
description: "Adds a command and a tool",
|
||||
register(api: OpenClawPluginApi) {
|
||||
api.registerCommand({
|
||||
name: "example",
|
||||
description: "Show plugin status",
|
||||
handler: async () => ({ text: "example ok" }),
|
||||
});
|
||||
|
||||
api.registerTool({
|
||||
name: "example_lookup",
|
||||
description: "Look up Example data",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
query: { type: "string" },
|
||||
},
|
||||
required: ["query"],
|
||||
},
|
||||
async execute(_callId, params) {
|
||||
return {
|
||||
content: [{ type: "text", text: `lookup: ${String(params.query)}` }],
|
||||
};
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## `defineChannelPluginEntry(...)`
|
||||
|
||||
Use this for a plugin that registers a `ChannelPlugin`.
|
||||
|
||||
```ts
|
||||
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
|
||||
import { channelPlugin } from "./src/channel.js";
|
||||
import { setRuntime } from "./src/runtime.js";
|
||||
|
||||
export default defineChannelPluginEntry({
|
||||
id: "example-channel",
|
||||
name: "Example Channel",
|
||||
description: "Example messaging plugin",
|
||||
plugin: channelPlugin,
|
||||
setRuntime,
|
||||
registerFull(api) {
|
||||
api.registerTool({
|
||||
name: "example_channel_status",
|
||||
description: "Inspect Example Channel state",
|
||||
parameters: { type: "object", properties: {} },
|
||||
async execute() {
|
||||
return { content: [{ type: "text", text: "ok" }] };
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### Why `registerFull(...)` exists
|
||||
|
||||
OpenClaw can load plugins in setup-focused registration modes. `registerFull`
|
||||
lets a channel plugin skip extra runtime-only registrations such as tools while
|
||||
still registering the channel capability itself.
|
||||
|
||||
Use it for:
|
||||
|
||||
- agent tools
|
||||
- gateway-only routes
|
||||
- runtime-only commands
|
||||
|
||||
Do not use it for the actual `ChannelPlugin`; that belongs in `plugin: ...`.
|
||||
|
||||
## `defineSetupPluginEntry(...)`
|
||||
|
||||
Use this when a channel ships a second module for setup flows.
|
||||
|
||||
```ts
|
||||
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core";
|
||||
import { exampleSetupPlugin } from "./src/channel.setup.js";
|
||||
|
||||
export default defineSetupPluginEntry(exampleSetupPlugin);
|
||||
```
|
||||
|
||||
This keeps the setup entry shape explicit and matches the bundled channel
|
||||
pattern used in OpenClaw.
|
||||
|
||||
## One plugin, many capabilities
|
||||
|
||||
A single entry file can register multiple capabilities:
|
||||
|
||||
```ts
|
||||
import { definePluginEntry, type OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-entry";
|
||||
|
||||
export default definePluginEntry({
|
||||
id: "example-hybrid",
|
||||
name: "Example Hybrid",
|
||||
description: "Provider plus tools",
|
||||
register(api: OpenClawPluginApi) {
|
||||
api.registerProvider({
|
||||
id: "example",
|
||||
label: "Example",
|
||||
auth: [],
|
||||
});
|
||||
|
||||
api.registerTool({
|
||||
name: "example_ping",
|
||||
description: "Simple health check",
|
||||
parameters: { type: "object", properties: {} },
|
||||
async execute() {
|
||||
return { content: [{ type: "text", text: "pong" }] };
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Entry-file checklist
|
||||
|
||||
- Give the plugin a stable `id`.
|
||||
- Keep `name` and `description` human-readable.
|
||||
- Put schema at the entry level when the plugin has config.
|
||||
- Register only public capabilities inside `register(api)`.
|
||||
- Keep channel plugins on `plugin-sdk/core`.
|
||||
- Keep non-channel plugins on `plugin-sdk/plugin-entry`.
|
||||
|
||||
## Related
|
||||
|
||||
- [Plugin SDK Overview](/plugins/sdk-overview)
|
||||
- [Plugin Runtime](/plugins/sdk-runtime)
|
||||
- [Channel Plugin SDK](/plugins/sdk-channel-plugins)
|
||||
- [Provider Plugin SDK](/plugins/sdk-provider-plugins)
|
||||
@@ -165,5 +165,6 @@ This is a temporary escape hatch, not a permanent solution.
|
||||
## Related
|
||||
|
||||
- [Building Plugins](/plugins/building-plugins)
|
||||
- [Plugin SDK Overview](/plugins/sdk-overview)
|
||||
- [Plugin Internals](/plugins/architecture)
|
||||
- [Plugin Manifest](/plugins/manifest)
|
||||
|
||||
175
docs/plugins/sdk-overview.md
Normal file
175
docs/plugins/sdk-overview.md
Normal file
@@ -0,0 +1,175 @@
|
||||
---
|
||||
title: "Plugin SDK Overview"
|
||||
sidebarTitle: "SDK Overview"
|
||||
summary: "How the OpenClaw plugin SDK is organized, which subpaths are stable, and how to choose the right import"
|
||||
read_when:
|
||||
- You are starting a new OpenClaw plugin
|
||||
- You need to choose the right plugin-sdk subpath
|
||||
- You are replacing deprecated compat imports
|
||||
---
|
||||
|
||||
# Plugin SDK Overview
|
||||
|
||||
The OpenClaw plugin SDK is split into **small public subpaths** under
|
||||
`openclaw/plugin-sdk/<subpath>`.
|
||||
|
||||
Use the narrowest import that matches the job. That keeps plugin dependencies
|
||||
small, avoids circular imports, and makes it clear which contract you depend on.
|
||||
|
||||
## Rules first
|
||||
|
||||
- Use focused imports such as `openclaw/plugin-sdk/plugin-entry`.
|
||||
- Do not import the root `openclaw/plugin-sdk` barrel in new code.
|
||||
- Do not import `openclaw/extension-api` in new code.
|
||||
- Do not import `src/**` from plugin packages.
|
||||
- Inside a plugin package, route internal imports through local files such as
|
||||
`./api.ts` or `./runtime-api.ts`, not through the published SDK path for that
|
||||
same plugin.
|
||||
|
||||
## SDK map
|
||||
|
||||
| Job | Subpath | Next page |
|
||||
| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- |
|
||||
| Define plugin entry modules | `plugin-sdk/plugin-entry`, `plugin-sdk/core` | [Plugin Entry Points](/plugins/sdk-entrypoints) |
|
||||
| Use injected runtime helpers | `plugin-sdk/runtime`, `plugin-sdk/runtime-store` | [Plugin Runtime](/plugins/sdk-runtime) |
|
||||
| Build setup/configure flows | `plugin-sdk/setup`, `plugin-sdk/channel-setup`, `plugin-sdk/secret-input` | [Plugin Setup](/plugins/sdk-setup) |
|
||||
| Build channel plugins | `plugin-sdk/core`, `plugin-sdk/channel-contract`, `plugin-sdk/channel-actions`, `plugin-sdk/channel-pairing` | [Channel Plugin SDK](/plugins/sdk-channel-plugins) |
|
||||
| Build provider plugins | `plugin-sdk/plugin-entry`, `plugin-sdk/provider-auth`, `plugin-sdk/provider-onboard`, `plugin-sdk/provider-models`, `plugin-sdk/provider-usage` | [Provider Plugin SDK](/plugins/sdk-provider-plugins) |
|
||||
| Test plugin code | `plugin-sdk/testing` | [Plugin SDK Testing](/plugins/sdk-testing) |
|
||||
|
||||
## Typical plugin layout
|
||||
|
||||
```text
|
||||
my-plugin/
|
||||
├── package.json
|
||||
├── openclaw.plugin.json
|
||||
├── index.ts
|
||||
├── setup-entry.ts
|
||||
├── api.ts
|
||||
├── runtime-api.ts
|
||||
└── src/
|
||||
├── provider.ts
|
||||
├── setup.ts
|
||||
└── provider.test.ts
|
||||
```
|
||||
|
||||
```ts
|
||||
// api.ts
|
||||
export {
|
||||
definePluginEntry,
|
||||
type OpenClawPluginApi,
|
||||
type ProviderAuthContext,
|
||||
type ProviderAuthResult,
|
||||
} from "openclaw/plugin-sdk/plugin-entry";
|
||||
```
|
||||
|
||||
## What belongs where
|
||||
|
||||
### Entry helpers
|
||||
|
||||
- `plugin-sdk/plugin-entry` is the default entry surface for providers, tools,
|
||||
commands, services, memory plugins, and context engines.
|
||||
- `plugin-sdk/core` adds channel-focused helpers such as
|
||||
`defineChannelPluginEntry(...)`.
|
||||
|
||||
### Runtime helpers
|
||||
|
||||
- Use `api.runtime.*` for trusted in-process helpers that OpenClaw injects at
|
||||
registration time.
|
||||
- Use `plugin-sdk/runtime-store` when plugin modules need a mutable runtime slot
|
||||
that is initialized later.
|
||||
|
||||
### Setup helpers
|
||||
|
||||
- `plugin-sdk/setup` contains shared setup-wizard helpers and config patch
|
||||
helpers.
|
||||
- `plugin-sdk/channel-setup` contains channel-specific setup adapters.
|
||||
- `plugin-sdk/secret-input` exposes the shared secret-input schema helpers.
|
||||
|
||||
### Channel helpers
|
||||
|
||||
- `plugin-sdk/channel-contract` exports pure channel types.
|
||||
- `plugin-sdk/channel-actions` covers shared `message` tool schema helpers.
|
||||
- `plugin-sdk/channel-pairing` covers pairing approval flows.
|
||||
- `plugin-sdk/webhook-ingress` covers plugin-owned webhook routes.
|
||||
|
||||
### Provider helpers
|
||||
|
||||
- `plugin-sdk/provider-auth` covers auth flows and credential helpers.
|
||||
- `plugin-sdk/provider-onboard` covers config patches after auth/setup.
|
||||
- `plugin-sdk/provider-models` covers catalog and model-definition helpers.
|
||||
- `plugin-sdk/provider-usage` covers usage snapshot helpers.
|
||||
- `plugin-sdk/provider-setup` and `plugin-sdk/self-hosted-provider-setup`
|
||||
cover self-hosted and local-model onboarding.
|
||||
|
||||
## Example: mixing subpaths in one plugin
|
||||
|
||||
```ts
|
||||
import { definePluginEntry, type OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-entry";
|
||||
import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth";
|
||||
import { applyProviderConfigWithDefaultModel } from "openclaw/plugin-sdk/provider-onboard";
|
||||
import { buildSecretInputSchema } from "openclaw/plugin-sdk/secret-input";
|
||||
|
||||
export default definePluginEntry({
|
||||
id: "example-provider",
|
||||
name: "Example Provider",
|
||||
description: "Small provider plugin example",
|
||||
configSchema: {
|
||||
jsonSchema: {
|
||||
type: "object",
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
apiKey: { type: "string" },
|
||||
},
|
||||
},
|
||||
safeParse(value) {
|
||||
return buildSecretInputSchema().safeParse((value as { apiKey?: unknown })?.apiKey);
|
||||
},
|
||||
},
|
||||
register(api: OpenClawPluginApi) {
|
||||
api.registerProvider({
|
||||
id: "example",
|
||||
label: "Example",
|
||||
auth: [
|
||||
createProviderApiKeyAuthMethod({
|
||||
providerId: "example",
|
||||
methodId: "api-key",
|
||||
label: "Example API key",
|
||||
optionKey: "exampleApiKey",
|
||||
flagName: "--example-api-key",
|
||||
envVar: "EXAMPLE_API_KEY",
|
||||
promptMessage: "Enter Example API key",
|
||||
profileId: "example:default",
|
||||
defaultModel: "example/default",
|
||||
applyConfig: (cfg) =>
|
||||
applyProviderConfigWithDefaultModel(cfg, "example", {
|
||||
id: "default",
|
||||
name: "Default",
|
||||
}),
|
||||
}),
|
||||
],
|
||||
});
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Choose the smallest public seam
|
||||
|
||||
If a helper exists on a focused subpath, prefer that over a broader runtime
|
||||
surface.
|
||||
|
||||
- Prefer `plugin-sdk/provider-auth` over reaching into unrelated provider files.
|
||||
- Prefer `plugin-sdk/channel-contract` for types in tests and helper modules.
|
||||
- Prefer `plugin-sdk/runtime-store` over custom mutable globals.
|
||||
- Prefer `plugin-sdk/testing` for shared test fixtures.
|
||||
|
||||
## Related
|
||||
|
||||
- [Building Plugins](/plugins/building-plugins)
|
||||
- [Plugin Entry Points](/plugins/sdk-entrypoints)
|
||||
- [Plugin Runtime](/plugins/sdk-runtime)
|
||||
- [Plugin Setup](/plugins/sdk-setup)
|
||||
- [Channel Plugin SDK](/plugins/sdk-channel-plugins)
|
||||
- [Provider Plugin SDK](/plugins/sdk-provider-plugins)
|
||||
- [Plugin SDK Testing](/plugins/sdk-testing)
|
||||
- [Plugin SDK Migration](/plugins/sdk-migration)
|
||||
184
docs/plugins/sdk-provider-plugins.md
Normal file
184
docs/plugins/sdk-provider-plugins.md
Normal file
@@ -0,0 +1,184 @@
|
||||
---
|
||||
title: "Provider Plugin SDK"
|
||||
sidebarTitle: "Provider Plugins"
|
||||
summary: "Contracts and helper subpaths for model-provider plugins, including auth, onboarding, catalogs, and usage"
|
||||
read_when:
|
||||
- You are building a model provider plugin
|
||||
- You need auth helpers for API keys or OAuth
|
||||
- You need onboarding config patches or catalog helpers
|
||||
---
|
||||
|
||||
# Provider Plugin SDK
|
||||
|
||||
Provider plugins use `definePluginEntry(...)` and call `api.registerProvider(...)`
|
||||
with a `ProviderPlugin` definition.
|
||||
|
||||
## Minimal provider entry
|
||||
|
||||
```ts
|
||||
import { definePluginEntry, type OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-entry";
|
||||
|
||||
export default definePluginEntry({
|
||||
id: "example-provider",
|
||||
name: "Example Provider",
|
||||
description: "Example text-inference provider plugin",
|
||||
register(api: OpenClawPluginApi) {
|
||||
api.registerProvider({
|
||||
id: "example",
|
||||
label: "Example",
|
||||
auth: [],
|
||||
});
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Provider subpaths
|
||||
|
||||
| Subpath | Use it for |
|
||||
| --------------------------------------- | ---------------------------------------------- |
|
||||
| `plugin-sdk/provider-auth` | API key, OAuth, auth-profile, and PKCE helpers |
|
||||
| `plugin-sdk/provider-onboard` | Config patches after setup/auth |
|
||||
| `plugin-sdk/provider-models` | Model-definition and catalog helpers |
|
||||
| `plugin-sdk/provider-setup` | Shared local/self-hosted setup flows |
|
||||
| `plugin-sdk/self-hosted-provider-setup` | OpenAI-compatible self-hosted providers |
|
||||
| `plugin-sdk/provider-usage` | Usage snapshot fetch helpers |
|
||||
|
||||
## API key auth
|
||||
|
||||
`createProviderApiKeyAuthMethod(...)` is the standard helper for API-key
|
||||
providers:
|
||||
|
||||
```ts
|
||||
import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth";
|
||||
import { applyProviderConfigWithDefaultModel } from "openclaw/plugin-sdk/provider-onboard";
|
||||
|
||||
const auth = [
|
||||
createProviderApiKeyAuthMethod({
|
||||
providerId: "example",
|
||||
methodId: "api-key",
|
||||
label: "Example API key",
|
||||
optionKey: "exampleApiKey",
|
||||
flagName: "--example-api-key",
|
||||
envVar: "EXAMPLE_API_KEY",
|
||||
promptMessage: "Enter Example API key",
|
||||
profileId: "example:default",
|
||||
defaultModel: "example/default",
|
||||
applyConfig: (cfg) =>
|
||||
applyProviderConfigWithDefaultModel(cfg, "example", {
|
||||
id: "default",
|
||||
name: "Default",
|
||||
}),
|
||||
}),
|
||||
];
|
||||
```
|
||||
|
||||
## OAuth auth
|
||||
|
||||
`buildOauthProviderAuthResult(...)` builds the standard auth result payload for
|
||||
OAuth-style providers:
|
||||
|
||||
```ts
|
||||
import { buildOauthProviderAuthResult } from "openclaw/plugin-sdk/provider-auth";
|
||||
|
||||
async function runOAuthLogin() {
|
||||
return buildOauthProviderAuthResult({
|
||||
providerId: "example-portal",
|
||||
defaultModel: "example-portal/default",
|
||||
access: "access-token",
|
||||
refresh: "refresh-token",
|
||||
email: "user@example.com",
|
||||
notes: ["Tokens auto-refresh when the provider supports refresh tokens."],
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
## Catalog and discovery hooks
|
||||
|
||||
Provider plugins usually implement either `catalog` or the legacy `discovery`
|
||||
alias. `catalog` is preferred.
|
||||
|
||||
```ts
|
||||
api.registerProvider({
|
||||
id: "example",
|
||||
label: "Example",
|
||||
auth,
|
||||
catalog: {
|
||||
order: "simple",
|
||||
async run(ctx) {
|
||||
const apiKey = ctx.resolveProviderApiKey("example").apiKey;
|
||||
if (!apiKey) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
provider: {
|
||||
api: "openai",
|
||||
baseUrl: "https://api.example.com/v1",
|
||||
apiKey,
|
||||
models: [
|
||||
{
|
||||
id: "default",
|
||||
name: "Default",
|
||||
input: ["text"],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Onboarding config patches
|
||||
|
||||
`plugin-sdk/provider-onboard` keeps post-auth config writes consistent.
|
||||
|
||||
Common helpers:
|
||||
|
||||
- `applyProviderConfigWithDefaultModel(...)`
|
||||
- `applyProviderConfigWithDefaultModels(...)`
|
||||
- `applyProviderConfigWithModelCatalog(...)`
|
||||
- `applyAgentDefaultModelPrimary(...)`
|
||||
- `ensureModelAllowlistEntry(...)`
|
||||
|
||||
## Self-hosted and local model setup
|
||||
|
||||
Use `plugin-sdk/provider-setup` or
|
||||
`plugin-sdk/self-hosted-provider-setup` when the provider is an OpenAI-style
|
||||
backend, Ollama, SGLang, or vLLM.
|
||||
|
||||
Examples from the shared setup surfaces:
|
||||
|
||||
- `promptAndConfigureOllama(...)`
|
||||
- `configureOllamaNonInteractive(...)`
|
||||
- `promptAndConfigureOpenAICompatibleSelfHostedProvider(...)`
|
||||
- `discoverOpenAICompatibleSelfHostedProvider(...)`
|
||||
|
||||
These helpers keep setup behavior aligned with built-in provider flows.
|
||||
|
||||
## Usage snapshots
|
||||
|
||||
If the provider owns quota or usage endpoints, use `resolveUsageAuth(...)` and
|
||||
`fetchUsageSnapshot(...)`.
|
||||
|
||||
`plugin-sdk/provider-usage` includes shared fetch helpers such as:
|
||||
|
||||
- `fetchClaudeUsage(...)`
|
||||
- `fetchCodexUsage(...)`
|
||||
- `fetchGeminiUsage(...)`
|
||||
- `fetchMinimaxUsage(...)`
|
||||
- `fetchZaiUsage(...)`
|
||||
|
||||
## Provider guidance
|
||||
|
||||
- Keep auth logic in `provider-auth`.
|
||||
- Keep config mutation in `provider-onboard`.
|
||||
- Keep catalog/model helpers in `provider-models`.
|
||||
- Keep usage logic in `provider-usage`.
|
||||
- Use `catalog`, not `discovery`, in new plugins.
|
||||
|
||||
## Related
|
||||
|
||||
- [Plugin SDK Overview](/plugins/sdk-overview)
|
||||
- [Plugin Entry Points](/plugins/sdk-entrypoints)
|
||||
- [Plugin Setup](/plugins/sdk-setup)
|
||||
- [Plugin Internals](/plugins/architecture#provider-runtime-hooks)
|
||||
156
docs/plugins/sdk-runtime.md
Normal file
156
docs/plugins/sdk-runtime.md
Normal file
@@ -0,0 +1,156 @@
|
||||
---
|
||||
title: "Plugin Runtime"
|
||||
sidebarTitle: "Runtime"
|
||||
summary: "How `api.runtime` works, when to use it, and how to manage plugin runtime state safely"
|
||||
read_when:
|
||||
- You need to call runtime helpers from a plugin
|
||||
- You are deciding between hooks and injected runtime
|
||||
- You need a safe module-level runtime store
|
||||
---
|
||||
|
||||
# Plugin Runtime
|
||||
|
||||
Native OpenClaw plugins receive a trusted runtime through `api.runtime`.
|
||||
|
||||
Use it for **host-owned operations** that should stay inside OpenClaw’s runtime:
|
||||
|
||||
- reading and writing config
|
||||
- agent/session helpers
|
||||
- system commands with OpenClaw timeouts
|
||||
- media, speech, image-generation, and web-search runtime calls
|
||||
- channel-owned helpers for bundled channel plugins
|
||||
|
||||
## When to use runtime vs focused SDK helpers
|
||||
|
||||
- Use focused SDK helpers when a public subpath already models the job.
|
||||
- Use `api.runtime.*` when the host owns the operation or state.
|
||||
- Prefer hooks for loose integrations that do not need tight in-process access.
|
||||
|
||||
## Runtime namespaces
|
||||
|
||||
| Namespace | What it covers |
|
||||
| -------------------------------- | -------------------------------------------------- |
|
||||
| `api.runtime.config` | Load and persist OpenClaw config |
|
||||
| `api.runtime.agent` | Agent workspace, identity, timeouts, session store |
|
||||
| `api.runtime.system` | System events, heartbeats, command execution |
|
||||
| `api.runtime.media` | File/media loading and transforms |
|
||||
| `api.runtime.tts` | Speech synthesis and voice listing |
|
||||
| `api.runtime.mediaUnderstanding` | Image/audio/video understanding |
|
||||
| `api.runtime.imageGeneration` | Image generation providers |
|
||||
| `api.runtime.webSearch` | Runtime web-search execution |
|
||||
| `api.runtime.modelAuth` | Resolve model/provider credentials |
|
||||
| `api.runtime.subagent` | Spawn, wait, inspect, and delete subagent sessions |
|
||||
| `api.runtime.channel` | Channel-heavy helpers for native channel plugins |
|
||||
|
||||
## Example: read and persist config
|
||||
|
||||
```ts
|
||||
import { definePluginEntry, type OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-entry";
|
||||
|
||||
export default definePluginEntry({
|
||||
id: "talk-settings",
|
||||
name: "Talk Settings",
|
||||
description: "Example runtime config write",
|
||||
register(api: OpenClawPluginApi) {
|
||||
api.registerCommand({
|
||||
name: "talk-mode",
|
||||
description: "Enable talk mode",
|
||||
handler: async () => {
|
||||
const cfg = api.runtime.config.loadConfig();
|
||||
const nextConfig = {
|
||||
...cfg,
|
||||
talk: {
|
||||
...cfg.talk,
|
||||
enabled: true,
|
||||
},
|
||||
};
|
||||
await api.runtime.config.writeConfigFile(nextConfig);
|
||||
return { text: "talk mode enabled" };
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Example: use a runtime service owned by OpenClaw
|
||||
|
||||
```ts
|
||||
const cfg = api.runtime.config.loadConfig();
|
||||
const voices = await api.runtime.tts.listVoices({
|
||||
provider: "openai",
|
||||
cfg,
|
||||
});
|
||||
|
||||
return {
|
||||
text: voices.map((voice) => `${voice.name ?? voice.id}: ${voice.id}`).join("\n"),
|
||||
};
|
||||
```
|
||||
|
||||
## `createPluginRuntimeStore(...)`
|
||||
|
||||
Plugin modules often need a small mutable slot for runtime-backed helpers. Use
|
||||
`plugin-sdk/runtime-store` instead of an unguarded `let runtime`.
|
||||
|
||||
```ts
|
||||
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
|
||||
import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
|
||||
import { channelPlugin } from "./src/channel.js";
|
||||
|
||||
const runtimeStore = createPluginRuntimeStore<{
|
||||
logger: { info(message: string): void };
|
||||
}>("Example Channel runtime not initialized");
|
||||
|
||||
export function setExampleRuntime(runtime: { logger: { info(message: string): void } }) {
|
||||
runtimeStore.setRuntime(runtime);
|
||||
}
|
||||
|
||||
export function getExampleRuntime() {
|
||||
return runtimeStore.getRuntime();
|
||||
}
|
||||
|
||||
export default defineChannelPluginEntry({
|
||||
id: "example-channel",
|
||||
name: "Example Channel",
|
||||
description: "Example runtime store usage",
|
||||
plugin: channelPlugin,
|
||||
setRuntime: setExampleRuntime,
|
||||
});
|
||||
```
|
||||
|
||||
`createPluginRuntimeStore(...)` gives you:
|
||||
|
||||
- `setRuntime(next)`
|
||||
- `clearRuntime()`
|
||||
- `tryGetRuntime()`
|
||||
- `getRuntime()`
|
||||
|
||||
`getRuntime()` throws with your custom message if the runtime was never set.
|
||||
|
||||
## Channel runtime note
|
||||
|
||||
`api.runtime.channel.*` is the heaviest namespace. It exists for native channel
|
||||
plugins that need tight coupling with the OpenClaw messaging stack.
|
||||
|
||||
Prefer narrower subpaths such as:
|
||||
|
||||
- `plugin-sdk/channel-pairing`
|
||||
- `plugin-sdk/channel-actions`
|
||||
- `plugin-sdk/channel-feedback`
|
||||
- `plugin-sdk/channel-lifecycle`
|
||||
|
||||
Use `api.runtime.channel.*` when the operation is clearly host-owned and there
|
||||
is no smaller public seam.
|
||||
|
||||
## Runtime safety guidelines
|
||||
|
||||
- Do not cache config snapshots longer than needed.
|
||||
- Prefer `createPluginRuntimeStore(...)` for shared module state.
|
||||
- Keep runtime-backed code behind small local helpers.
|
||||
- Avoid reaching into runtime namespaces you do not need.
|
||||
|
||||
## Related
|
||||
|
||||
- [Plugin SDK Overview](/plugins/sdk-overview)
|
||||
- [Plugin Entry Points](/plugins/sdk-entrypoints)
|
||||
- [Plugin Setup](/plugins/sdk-setup)
|
||||
- [Channel Plugin SDK](/plugins/sdk-channel-plugins)
|
||||
132
docs/plugins/sdk-setup.md
Normal file
132
docs/plugins/sdk-setup.md
Normal file
@@ -0,0 +1,132 @@
|
||||
---
|
||||
title: "Plugin Setup"
|
||||
sidebarTitle: "Setup"
|
||||
summary: "Shared setup-wizard helpers for channel plugins, provider plugins, and secret inputs"
|
||||
read_when:
|
||||
- You are building a setup or onboarding flow
|
||||
- You need shared allowlist or DM policy setup helpers
|
||||
- You need the shared secret-input schema
|
||||
---
|
||||
|
||||
# Plugin Setup
|
||||
|
||||
OpenClaw exposes shared setup helpers so plugin setup flows behave like the
|
||||
built-in ones.
|
||||
|
||||
Main subpaths:
|
||||
|
||||
- `openclaw/plugin-sdk/setup`
|
||||
- `openclaw/plugin-sdk/channel-setup`
|
||||
- `openclaw/plugin-sdk/secret-input`
|
||||
|
||||
## Channel setup helpers
|
||||
|
||||
Use `plugin-sdk/channel-setup` when a channel plugin needs the standard setup
|
||||
adapter and setup wizard shapes.
|
||||
|
||||
### Optional channel plugins
|
||||
|
||||
If a channel is installable but not always present, use
|
||||
`createOptionalChannelSetupSurface(...)`:
|
||||
|
||||
```ts
|
||||
import { createOptionalChannelSetupSurface } from "openclaw/plugin-sdk/channel-setup";
|
||||
|
||||
export const optionalExampleSetup = createOptionalChannelSetupSurface({
|
||||
channel: "example",
|
||||
label: "Example Channel",
|
||||
npmSpec: "@openclaw/example-channel",
|
||||
docsPath: "/channels/example",
|
||||
});
|
||||
```
|
||||
|
||||
That returns:
|
||||
|
||||
- `setupAdapter`
|
||||
- `setupWizard`
|
||||
|
||||
Both surfaces produce a consistent “install this plugin first” experience.
|
||||
|
||||
## Shared setup helpers
|
||||
|
||||
`plugin-sdk/setup` re-exports the setup primitives used by bundled channels.
|
||||
|
||||
Common helpers:
|
||||
|
||||
- `applySetupAccountConfigPatch(...)`
|
||||
- `createPatchedAccountSetupAdapter(...)`
|
||||
- `createEnvPatchedAccountSetupAdapter(...)`
|
||||
- `createTopLevelChannelDmPolicy(...)`
|
||||
- `setSetupChannelEnabled(...)`
|
||||
- `promptResolvedAllowFrom(...)`
|
||||
- `promptSingleChannelSecretInput(...)`
|
||||
|
||||
### Example: patch channel config in setup
|
||||
|
||||
```ts
|
||||
import {
|
||||
DEFAULT_ACCOUNT_ID,
|
||||
createPatchedAccountSetupAdapter,
|
||||
setSetupChannelEnabled,
|
||||
} from "openclaw/plugin-sdk/setup";
|
||||
|
||||
export const exampleSetupAdapter = createPatchedAccountSetupAdapter({
|
||||
resolveAccountId: ({ accountId }) => accountId ?? DEFAULT_ACCOUNT_ID,
|
||||
applyPatch: ({ nextConfig, accountId }) => {
|
||||
const resolvedAccountId = accountId ?? DEFAULT_ACCOUNT_ID;
|
||||
return setSetupChannelEnabled({
|
||||
nextConfig,
|
||||
channel: "example",
|
||||
accountId: resolvedAccountId,
|
||||
enabled: true,
|
||||
});
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Secret input schema
|
||||
|
||||
Use `plugin-sdk/secret-input` instead of rolling your own secret-input parser.
|
||||
|
||||
```ts
|
||||
import {
|
||||
buildOptionalSecretInputSchema,
|
||||
buildSecretInputArraySchema,
|
||||
buildSecretInputSchema,
|
||||
hasConfiguredSecretInput,
|
||||
} from "openclaw/plugin-sdk/secret-input";
|
||||
|
||||
const ApiKeySchema = buildSecretInputSchema();
|
||||
const OptionalApiKeySchema = buildOptionalSecretInputSchema();
|
||||
const ExtraKeysSchema = buildSecretInputArraySchema();
|
||||
|
||||
const parsed = OptionalApiKeySchema.safeParse(process.env.EXAMPLE_API_KEY);
|
||||
if (parsed.success && hasConfiguredSecretInput(parsed.data)) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## Provider setup note
|
||||
|
||||
Provider-specific onboarding helpers live on provider-focused subpaths:
|
||||
|
||||
- `plugin-sdk/provider-auth`
|
||||
- `plugin-sdk/provider-onboard`
|
||||
- `plugin-sdk/provider-setup`
|
||||
- `plugin-sdk/self-hosted-provider-setup`
|
||||
|
||||
See [Provider Plugin SDK](/plugins/sdk-provider-plugins).
|
||||
|
||||
## Setup guidance
|
||||
|
||||
- Keep setup input schemas strict and small.
|
||||
- Reuse OpenClaw’s allowlist, DM-policy, and secret-input helpers.
|
||||
- Keep setup-entry modules thin; move behavior into `src/`.
|
||||
- Link docs from setup flows when install or auth steps are manual.
|
||||
|
||||
## Related
|
||||
|
||||
- [Plugin SDK Overview](/plugins/sdk-overview)
|
||||
- [Plugin Entry Points](/plugins/sdk-entrypoints)
|
||||
- [Provider Plugin SDK](/plugins/sdk-provider-plugins)
|
||||
- [Plugin Manifest](/plugins/manifest)
|
||||
112
docs/plugins/sdk-testing.md
Normal file
112
docs/plugins/sdk-testing.md
Normal file
@@ -0,0 +1,112 @@
|
||||
---
|
||||
title: "Plugin SDK Testing"
|
||||
sidebarTitle: "Testing"
|
||||
summary: "How to test plugin code with the public testing helpers and small local test doubles"
|
||||
read_when:
|
||||
- You are writing tests for a plugin
|
||||
- You need fixtures for Windows command shims or shared routing failures
|
||||
- You want to know what the public testing surface includes
|
||||
---
|
||||
|
||||
# Plugin SDK Testing
|
||||
|
||||
OpenClaw keeps the public testing surface intentionally small.
|
||||
|
||||
Use `openclaw/plugin-sdk/testing` for helpers that are stable enough to support
|
||||
for plugin authors, and build small plugin-local doubles for everything else.
|
||||
|
||||
## Public testing helpers
|
||||
|
||||
Current helpers include:
|
||||
|
||||
- `createWindowsCmdShimFixture(...)`
|
||||
- `installCommonResolveTargetErrorCases(...)`
|
||||
- `shouldAckReaction(...)`
|
||||
- `removeAckReactionAfterReply(...)`
|
||||
|
||||
The testing surface also re-exports some shared types:
|
||||
|
||||
- `OpenClawConfig`
|
||||
- `PluginRuntime`
|
||||
- `RuntimeEnv`
|
||||
- `ChannelAccountSnapshot`
|
||||
- `ChannelGatewayContext`
|
||||
|
||||
## Example: Windows command shim fixture
|
||||
|
||||
```ts
|
||||
import { createWindowsCmdShimFixture } from "openclaw/plugin-sdk/testing";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
describe("example CLI integration", () => {
|
||||
it("creates a command shim", async () => {
|
||||
await createWindowsCmdShimFixture({
|
||||
shimPath: "/tmp/example.cmd",
|
||||
scriptPath: "/tmp/example.js",
|
||||
shimLine: 'node "%~dp0\\example.js" %*',
|
||||
});
|
||||
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Example: shared target-resolution failures
|
||||
|
||||
```ts
|
||||
import { installCommonResolveTargetErrorCases } from "openclaw/plugin-sdk/testing";
|
||||
|
||||
installCommonResolveTargetErrorCases({
|
||||
implicitAllowFrom: ["user-1"],
|
||||
resolveTarget({ to, mode, allowFrom }) {
|
||||
if (!to?.trim()) {
|
||||
return { ok: false, error: new Error("missing target") };
|
||||
}
|
||||
if (mode === "implicit" && allowFrom.length > 0 && to === "invalid-target") {
|
||||
return { ok: false, error: new Error("invalid target") };
|
||||
}
|
||||
return { ok: true, to };
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Runtime doubles
|
||||
|
||||
There is no catch-all `createTestRuntime()` export on the public SDK today.
|
||||
Instead:
|
||||
|
||||
- use the public testing helpers where they fit
|
||||
- use `plugin-sdk/runtime` for small runtime adapters
|
||||
- build tiny plugin-local runtime doubles for the rest
|
||||
|
||||
Example:
|
||||
|
||||
```ts
|
||||
import { createLoggerBackedRuntime } from "openclaw/plugin-sdk/runtime";
|
||||
|
||||
const logs: string[] = [];
|
||||
|
||||
const runtime = createLoggerBackedRuntime({
|
||||
logger: {
|
||||
info(message) {
|
||||
logs.push(`info:${message}`);
|
||||
},
|
||||
error(message) {
|
||||
logs.push(`error:${message}`);
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Test guidance
|
||||
|
||||
- Prefer focused unit tests over giant end-to-end harnesses.
|
||||
- Import pure types from focused SDK subpaths in tests.
|
||||
- Keep plugin-local test doubles small and explicit.
|
||||
- Avoid depending on non-exported OpenClaw test internals.
|
||||
|
||||
## Related
|
||||
|
||||
- [Building Plugins](/plugins/building-plugins)
|
||||
- [Plugin SDK Overview](/plugins/sdk-overview)
|
||||
- [Plugin Runtime](/plugins/sdk-runtime)
|
||||
@@ -56,6 +56,9 @@ OpenClaw recognizes two plugin formats:
|
||||
|
||||
Both show up under `openclaw plugins list`. See [Plugin Bundles](/plugins/bundles) for bundle details.
|
||||
|
||||
If you are writing a native plugin, start with [Building Plugins](/plugins/building-plugins)
|
||||
and the [Plugin SDK Overview](/plugins/sdk-overview).
|
||||
|
||||
## Official plugins
|
||||
|
||||
### Installable (npm)
|
||||
|
||||
@@ -14,10 +14,12 @@ export type ChannelId = ChatChannelId | (string & {});
|
||||
|
||||
export type ChannelOutboundTargetMode = "explicit" | "implicit" | "heartbeat";
|
||||
|
||||
/** Agent tool registered by a channel plugin. */
|
||||
export type ChannelAgentTool = AgentTool<TSchema, unknown> & {
|
||||
ownerOnly?: boolean;
|
||||
};
|
||||
|
||||
/** Lazy agent-tool factory used when tool availability depends on config. */
|
||||
export type ChannelAgentToolFactory = (params: { cfg?: OpenClawConfig }) => ChannelAgentTool[];
|
||||
|
||||
/**
|
||||
@@ -57,6 +59,7 @@ export type ChannelMessageToolDiscovery = {
|
||||
schema?: ChannelMessageToolSchemaContribution | ChannelMessageToolSchemaContribution[] | null;
|
||||
};
|
||||
|
||||
/** Shared setup input bag used by CLI, onboarding, and setup adapters. */
|
||||
export type ChannelSetupInput = {
|
||||
name?: string;
|
||||
token?: string;
|
||||
@@ -115,6 +118,7 @@ export type ChannelHeartbeatDeps = {
|
||||
hasActiveWebListener?: () => boolean;
|
||||
};
|
||||
|
||||
/** User-facing metadata used in docs, pickers, and setup surfaces. */
|
||||
export type ChannelMeta = {
|
||||
id: ChannelId;
|
||||
label: string;
|
||||
@@ -136,6 +140,7 @@ export type ChannelMeta = {
|
||||
preferOver?: string[];
|
||||
};
|
||||
|
||||
/** Snapshot row returned by channel status and lifecycle surfaces. */
|
||||
export type ChannelAccountSnapshot = {
|
||||
accountId: string;
|
||||
name?: string;
|
||||
@@ -220,6 +225,7 @@ export type ChannelGroupContext = {
|
||||
senderE164?: string | null;
|
||||
};
|
||||
|
||||
/** Static capability flags advertised by a channel plugin. */
|
||||
export type ChannelCapabilities = {
|
||||
chatTypes: Array<ChatType | "thread">;
|
||||
polls?: boolean;
|
||||
@@ -384,6 +390,7 @@ export type ChannelThreadingToolContext = {
|
||||
skipCrossContextDecoration?: boolean;
|
||||
};
|
||||
|
||||
/** Channel-owned messaging helpers for target parsing, routing, and payload shaping. */
|
||||
export type ChannelMessagingAdapter = {
|
||||
normalizeTarget?: (raw: string) => string | undefined;
|
||||
resolveSessionTarget?: (params: {
|
||||
@@ -470,6 +477,7 @@ export type ChannelDirectoryEntry = {
|
||||
|
||||
export type ChannelMessageActionName = ChannelMessageActionNameFromList;
|
||||
|
||||
/** Execution context passed to channel-owned actions on the shared `message` tool. */
|
||||
export type ChannelMessageActionContext = {
|
||||
channel: ChannelId;
|
||||
action: ChannelMessageActionName;
|
||||
@@ -503,6 +511,7 @@ export type ChannelToolSend = {
|
||||
threadId?: string | null;
|
||||
};
|
||||
|
||||
/** Channel-owned action surface for the shared `message` tool. */
|
||||
export type ChannelMessageActionAdapter = {
|
||||
/**
|
||||
* Unified discovery surface for the shared `message` tool.
|
||||
@@ -533,6 +542,7 @@ export type ChannelPollResult = {
|
||||
pollId?: string;
|
||||
};
|
||||
|
||||
/** Shared poll input after core has normalized the common poll model. */
|
||||
export type ChannelPollContext = {
|
||||
cfg: OpenClawConfig;
|
||||
to: string;
|
||||
|
||||
@@ -44,11 +44,13 @@ export type ChannelConfigUiHint = {
|
||||
itemTemplate?: unknown;
|
||||
};
|
||||
|
||||
/** JSON-schema-like config description published by a channel plugin. */
|
||||
export type ChannelConfigSchema = {
|
||||
schema: Record<string, unknown>;
|
||||
uiHints?: Record<string, ChannelConfigUiHint>;
|
||||
};
|
||||
|
||||
/** Full capability contract for a native channel plugin. */
|
||||
// oxlint-disable-next-line typescript/no-explicit-any
|
||||
export type ChannelPlugin<ResolvedAccount = any, Probe = unknown, Audit = unknown> = {
|
||||
id: ChannelId;
|
||||
|
||||
@@ -7,6 +7,7 @@ import { Type } from "@sinclair/typebox";
|
||||
import type { TSchema } from "@sinclair/typebox";
|
||||
import { stringEnum } from "../agents/schema/typebox.js";
|
||||
|
||||
/** Schema helper for channels that expose button rows on the shared `message` tool. */
|
||||
export function createMessageToolButtonsSchema(): TSchema {
|
||||
return Type.Array(
|
||||
Type.Array(
|
||||
@@ -22,6 +23,7 @@ export function createMessageToolButtonsSchema(): TSchema {
|
||||
);
|
||||
}
|
||||
|
||||
/** Schema helper for channels that accept provider-native card payloads. */
|
||||
export function createMessageToolCardSchema(): TSchema {
|
||||
return Type.Object(
|
||||
{},
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Pure channel contract types used by plugin implementations and tests.
|
||||
export type {
|
||||
BaseProbeResult,
|
||||
BaseTokenResolution,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Shared feedback helpers for typing indicators, ack reactions, and status reactions.
|
||||
export {
|
||||
removeAckReactionAfterReply,
|
||||
shouldAckReaction,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Shared inbound parsing helpers for channel plugins.
|
||||
export {
|
||||
createInboundDebouncer,
|
||||
resolveInboundDebounceMs,
|
||||
|
||||
@@ -10,12 +10,14 @@ import { createScopedPairingAccess } from "./pairing-access.js";
|
||||
|
||||
type ScopedPairingAccess = ReturnType<typeof createScopedPairingAccess>;
|
||||
|
||||
/** Pairing helpers scoped to one channel account. */
|
||||
export type ChannelPairingController = ScopedPairingAccess & {
|
||||
issueChallenge: (
|
||||
params: Omit<Parameters<typeof issuePairingChallenge>[0], "channel" | "upsertPairingRequest">,
|
||||
) => ReturnType<typeof issuePairingChallenge>;
|
||||
};
|
||||
|
||||
/** Pre-bind the channel id and storage sink for pairing challenges. */
|
||||
export function createChannelPairingChallengeIssuer(params: {
|
||||
channel: ChannelId;
|
||||
upsertPairingRequest: Parameters<typeof issuePairingChallenge>[0]["upsertPairingRequest"];
|
||||
@@ -33,6 +35,7 @@ export function createChannelPairingChallengeIssuer(params: {
|
||||
});
|
||||
}
|
||||
|
||||
/** Build the full scoped pairing controller used by channel runtime code. */
|
||||
export function createChannelPairingController(params: {
|
||||
core: PluginRuntime;
|
||||
channel: ChannelId;
|
||||
|
||||
@@ -17,6 +17,7 @@ export {
|
||||
splitSetupEntries,
|
||||
} from "./setup.js";
|
||||
|
||||
/** Metadata used to advertise an optional channel plugin during setup flows. */
|
||||
type OptionalChannelSetupParams = {
|
||||
channel: string;
|
||||
label: string;
|
||||
@@ -24,6 +25,7 @@ type OptionalChannelSetupParams = {
|
||||
docsPath?: string;
|
||||
};
|
||||
|
||||
/** Paired setup adapter + setup wizard for channels that may not be installed yet. */
|
||||
export type OptionalChannelSetupSurface = {
|
||||
setupAdapter: ChannelSetupAdapter;
|
||||
setupWizard: ChannelSetupWizard;
|
||||
@@ -34,6 +36,7 @@ export {
|
||||
createOptionalChannelSetupWizard,
|
||||
} from "./optional-channel-setup.js";
|
||||
|
||||
/** Build both optional setup surfaces from one metadata object. */
|
||||
export function createOptionalChannelSetupSurface(
|
||||
params: OptionalChannelSetupParams,
|
||||
): OptionalChannelSetupSurface {
|
||||
|
||||
@@ -137,6 +137,7 @@ export type ChannelOutboundSessionRouteParams = Parameters<
|
||||
NonNullable<ChannelMessagingAdapter["resolveOutboundSessionRoute"]>
|
||||
>[0];
|
||||
|
||||
/** Remove one of the known provider prefixes from a free-form target string. */
|
||||
export function stripChannelTargetPrefix(raw: string, ...providers: string[]): string {
|
||||
const trimmed = raw.trim();
|
||||
for (const provider of providers) {
|
||||
@@ -148,10 +149,15 @@ export function stripChannelTargetPrefix(raw: string, ...providers: string[]): s
|
||||
return trimmed;
|
||||
}
|
||||
|
||||
/** Remove generic target-kind prefixes such as `user:` or `group:`. */
|
||||
export function stripTargetKindPrefix(raw: string): string {
|
||||
return raw.replace(/^(user|channel|group|conversation|room|dm):/i, "").trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the canonical outbound session route payload returned by channel
|
||||
* message adapters.
|
||||
*/
|
||||
export function buildChannelOutboundSessionRoute(params: {
|
||||
cfg: OpenClawConfig;
|
||||
agentId: string;
|
||||
@@ -181,6 +187,7 @@ export function buildChannelOutboundSessionRoute(params: {
|
||||
};
|
||||
}
|
||||
|
||||
/** Options for a channel plugin entry that should register a channel capability. */
|
||||
type DefineChannelPluginEntryOptions<TPlugin extends ChannelPlugin = ChannelPlugin> = {
|
||||
id: string;
|
||||
name: string;
|
||||
@@ -227,7 +234,13 @@ type CreatedChannelPluginBase<TResolvedAccount> = Pick<
|
||||
>
|
||||
>;
|
||||
|
||||
// Shared channel-plugin entry boilerplate for bundled and third-party channels.
|
||||
/**
|
||||
* Canonical entry helper for channel plugins.
|
||||
*
|
||||
* This wraps `definePluginEntry(...)`, registers the channel capability, and
|
||||
* optionally exposes extra full-runtime registration such as tools or gateway
|
||||
* handlers that only make sense outside setup-only registration modes.
|
||||
*/
|
||||
export function defineChannelPluginEntry<TPlugin extends ChannelPlugin>({
|
||||
id,
|
||||
name,
|
||||
@@ -253,7 +266,12 @@ export function defineChannelPluginEntry<TPlugin extends ChannelPlugin>({
|
||||
});
|
||||
}
|
||||
|
||||
// Shared setup-entry shape so bundled channels do not duplicate `{ plugin }`.
|
||||
/**
|
||||
* Minimal setup-entry helper for channels that ship a separate `setup-entry.ts`.
|
||||
*
|
||||
* The setup entry only needs to export `{ plugin }`, but using this helper
|
||||
* keeps the shape explicit in examples and generated typings.
|
||||
*/
|
||||
export function defineSetupPluginEntry<TPlugin>(plugin: TPlugin) {
|
||||
return { plugin };
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ export type { OpenClawConfig } from "../config/config.js";
|
||||
|
||||
export { emptyPluginConfigSchema } from "../plugins/config-schema.js";
|
||||
|
||||
/** Options for a plugin entry that registers providers, tools, commands, or services. */
|
||||
type DefinePluginEntryOptions = {
|
||||
id: string;
|
||||
name: string;
|
||||
@@ -61,6 +62,7 @@ type DefinePluginEntryOptions = {
|
||||
register: (api: OpenClawPluginApi) => void;
|
||||
};
|
||||
|
||||
/** Normalized object shape that OpenClaw loads from a plugin entry module. */
|
||||
type DefinedPluginEntry = {
|
||||
id: string;
|
||||
name: string;
|
||||
@@ -69,13 +71,20 @@ type DefinedPluginEntry = {
|
||||
register: NonNullable<OpenClawPluginDefinition["register"]>;
|
||||
} & Pick<OpenClawPluginDefinition, "kind">;
|
||||
|
||||
/** Resolve either a concrete config schema or a lazy schema factory. */
|
||||
function resolvePluginConfigSchema(
|
||||
configSchema: DefinePluginEntryOptions["configSchema"] = emptyPluginConfigSchema,
|
||||
): OpenClawPluginConfigSchema {
|
||||
return typeof configSchema === "function" ? configSchema() : configSchema;
|
||||
}
|
||||
|
||||
// Small entry surface for provider and command plugins that do not need channel helpers.
|
||||
/**
|
||||
* Canonical entry helper for non-channel plugins.
|
||||
*
|
||||
* Use this for provider, tool, command, service, memory, and context-engine
|
||||
* plugins. Channel plugins should use `defineChannelPluginEntry(...)` from
|
||||
* `openclaw/plugin-sdk/core` so they inherit the channel capability wiring.
|
||||
*/
|
||||
export function definePluginEntry({
|
||||
id,
|
||||
name,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Public auth/onboarding helpers for provider plugins.
|
||||
// Curated auth + onboarding helpers for provider plugins.
|
||||
// Keep this surface focused on reusable provider-owned login flows.
|
||||
|
||||
export type { OpenClawConfig } from "../config/config.js";
|
||||
export type { SecretInput } from "../config/types.secrets.js";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Public config patch helpers for provider onboarding flows.
|
||||
// Curated config-patch helpers for provider onboarding flows.
|
||||
|
||||
export type { OpenClawConfig } from "../config/config.js";
|
||||
export type {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Curated setup helpers for provider plugins that integrate local/self-hosted models.
|
||||
export type {
|
||||
OpenClawPluginApi,
|
||||
ProviderAuthContext,
|
||||
|
||||
@@ -19,6 +19,7 @@ export * from "../logging.js";
|
||||
export { waitForAbortSignal } from "../infra/abort-signal.js";
|
||||
export { registerUnhandledRejectionHandler } from "../infra/unhandled-rejections.js";
|
||||
|
||||
/** Minimal logger contract accepted by runtime-adapter helpers. */
|
||||
type LoggerLike = {
|
||||
info: (message: string) => void;
|
||||
error: (message: string) => void;
|
||||
|
||||
@@ -14,10 +14,12 @@ export {
|
||||
normalizeSecretInputString,
|
||||
};
|
||||
|
||||
/** Optional version of the shared secret-input schema. */
|
||||
export function buildOptionalSecretInputSchema() {
|
||||
return buildSecretInputSchema().optional();
|
||||
}
|
||||
|
||||
/** Array version of the shared secret-input schema. */
|
||||
export function buildSecretInputArraySchema() {
|
||||
return z.array(buildSecretInputSchema());
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Focused self-hosted provider setup helpers for OpenAI-compatible backends.
|
||||
export type {
|
||||
OpenClawPluginApi,
|
||||
ProviderAuthContext,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Shared setup wizard/types/helpers for extension setup surfaces and adapters.
|
||||
// Shared setup wizard/types/helpers for plugin and channel setup surfaces.
|
||||
|
||||
export type { OpenClawConfig } from "../config/config.js";
|
||||
export type { DmPolicy, GroupPolicy } from "../config/types.js";
|
||||
|
||||
@@ -12,6 +12,7 @@ export type { PluginRuntime } from "../plugins/runtime/types.js";
|
||||
export type { RuntimeEnv } from "../runtime.js";
|
||||
export type { MockFn } from "../test-utils/vitest-mock-fn.js";
|
||||
|
||||
/** Create a tiny Windows `.cmd` shim fixture for plugin tests that spawn CLIs. */
|
||||
export async function createWindowsCmdShimFixture(params: {
|
||||
shimPath: string;
|
||||
scriptPath: string;
|
||||
@@ -37,6 +38,7 @@ type ResolveTargetFn = (params: {
|
||||
allowFrom: string[];
|
||||
}) => ResolveTargetResult;
|
||||
|
||||
/** Install a shared test matrix for target-resolution error handling. */
|
||||
export function installCommonResolveTargetErrorCases(params: {
|
||||
resolveTarget: ResolveTargetFn;
|
||||
implicitAllowFrom: string[];
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Curated webhook helpers for plugin-owned HTTP ingress and webhook targets.
|
||||
export {
|
||||
createBoundedCounter,
|
||||
createFixedWindowRateLimiter,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { LogLevel } from "../../logging/levels.js";
|
||||
|
||||
/** Structured logger surface injected into runtime-backed plugin helpers. */
|
||||
export type RuntimeLogger = {
|
||||
debug?: (message: string, meta?: Record<string, unknown>) => void;
|
||||
info: (message: string, meta?: Record<string, unknown>) => void;
|
||||
@@ -7,6 +8,7 @@ export type RuntimeLogger = {
|
||||
error: (message: string, meta?: Record<string, unknown>) => void;
|
||||
};
|
||||
|
||||
/** Core runtime helpers exposed to trusted native plugins. */
|
||||
export type PluginRuntimeCore = {
|
||||
version: string;
|
||||
config: {
|
||||
|
||||
@@ -50,6 +50,7 @@ export type SubagentDeleteSessionParams = {
|
||||
deleteTranscript?: boolean;
|
||||
};
|
||||
|
||||
/** Trusted in-process runtime surface injected into native plugins. */
|
||||
export type PluginRuntime = PluginRuntimeCore & {
|
||||
subagent: {
|
||||
run: (params: SubagentRunParams) => Promise<SubagentRunResult>;
|
||||
|
||||
@@ -55,6 +55,7 @@ export type ProviderAuthOptionBag = {
|
||||
[key: string]: unknown;
|
||||
};
|
||||
|
||||
/** Logger passed into plugin registration, services, and CLI surfaces. */
|
||||
export type PluginLogger = {
|
||||
debug?: (message: string) => void;
|
||||
info: (message: string) => void;
|
||||
@@ -77,6 +78,13 @@ export type PluginConfigValidation =
|
||||
| { ok: true; value?: unknown }
|
||||
| { ok: false; errors: string[] };
|
||||
|
||||
/**
|
||||
* Config schema contract accepted by plugin manifests and runtime registration.
|
||||
*
|
||||
* Plugins can provide a Zod-like parser, a lightweight `validate(...)`
|
||||
* function, or both. `uiHints` and `jsonSchema` are optional extras for docs,
|
||||
* forms, and config UIs.
|
||||
*/
|
||||
export type OpenClawPluginConfigSchema = {
|
||||
safeParse?: (value: unknown) => {
|
||||
success: boolean;
|
||||
@@ -91,6 +99,7 @@ export type OpenClawPluginConfigSchema = {
|
||||
jsonSchema?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
/** Trusted execution context passed to plugin-owned agent tool factories. */
|
||||
export type OpenClawPluginToolContext = {
|
||||
config?: OpenClawConfig;
|
||||
workspaceDir?: string;
|
||||
@@ -127,6 +136,7 @@ export type OpenClawPluginHookOptions = {
|
||||
|
||||
export type ProviderAuthKind = "oauth" | "api_key" | "token" | "device_code" | "custom";
|
||||
|
||||
/** Standard result payload returned by provider auth methods. */
|
||||
export type ProviderAuthResult = {
|
||||
profiles: Array<{ profileId: string; credential: AuthProfileCredential }>;
|
||||
/**
|
||||
@@ -141,6 +151,7 @@ export type ProviderAuthResult = {
|
||||
notes?: string[];
|
||||
};
|
||||
|
||||
/** Interactive auth context passed to provider login/setup methods. */
|
||||
export type ProviderAuthContext = {
|
||||
config: OpenClawConfig;
|
||||
agentDir?: string;
|
||||
@@ -612,12 +623,14 @@ export type ProviderPluginWizardSetup = {
|
||||
};
|
||||
};
|
||||
|
||||
/** Optional model-picker metadata shown in interactive provider selection flows. */
|
||||
export type ProviderPluginWizardModelPicker = {
|
||||
label?: string;
|
||||
hint?: string;
|
||||
methodId?: string;
|
||||
};
|
||||
|
||||
/** UI metadata that lets provider plugins appear in onboarding and configure flows. */
|
||||
export type ProviderPluginWizard = {
|
||||
setup?: ProviderPluginWizardSetup;
|
||||
modelPicker?: ProviderPluginWizardModelPicker;
|
||||
@@ -631,6 +644,7 @@ export type ProviderModelSelectedContext = {
|
||||
workspaceDir?: string;
|
||||
};
|
||||
|
||||
/** Text-inference provider capability registered by a plugin. */
|
||||
export type ProviderPlugin = {
|
||||
id: string;
|
||||
pluginId?: string;
|
||||
@@ -914,6 +928,7 @@ export type PluginWebSearchProviderEntry = WebSearchProviderPlugin & {
|
||||
pluginId: string;
|
||||
};
|
||||
|
||||
/** Speech capability registered by a plugin. */
|
||||
export type SpeechProviderPlugin = {
|
||||
id: SpeechProviderId;
|
||||
label: string;
|
||||
@@ -1255,6 +1270,7 @@ export type OpenClawPluginCliContext = {
|
||||
|
||||
export type OpenClawPluginCliRegistrar = (ctx: OpenClawPluginCliContext) => void | Promise<void>;
|
||||
|
||||
/** Context passed to long-lived plugin services. */
|
||||
export type OpenClawPluginServiceContext = {
|
||||
config: OpenClawConfig;
|
||||
workspaceDir?: string;
|
||||
@@ -1262,6 +1278,7 @@ export type OpenClawPluginServiceContext = {
|
||||
logger: PluginLogger;
|
||||
};
|
||||
|
||||
/** Background service registered by a plugin during `register(api)`. */
|
||||
export type OpenClawPluginService = {
|
||||
id: string;
|
||||
start: (ctx: OpenClawPluginServiceContext) => void | Promise<void>;
|
||||
@@ -1272,6 +1289,7 @@ export type OpenClawPluginChannelRegistration = {
|
||||
plugin: ChannelPlugin;
|
||||
};
|
||||
|
||||
/** Module-level plugin definition loaded from a native plugin entry file. */
|
||||
export type OpenClawPluginDefinition = {
|
||||
id?: string;
|
||||
name?: string;
|
||||
@@ -1289,6 +1307,7 @@ export type OpenClawPluginModule =
|
||||
|
||||
export type PluginRegistrationMode = "full" | "setup-only" | "setup-runtime";
|
||||
|
||||
/** Main registration API injected into native plugin entry files. */
|
||||
export type OpenClawPluginApi = {
|
||||
id: string;
|
||||
name: string;
|
||||
|
||||
Reference in New Issue
Block a user