Plugins (Extensions)

Schnellstart (neu bei Plugins?)

Ein Plugin ist einfach ein kleines Code-Modul, das OpenClaw um zusätzliche Funktionen erweitert (Befehle, Tools und Gateway-RPC).

Meistens wirst du Plugins verwenden, wenn du ein Feature willst, das noch nicht in Core-OpenClaw eingebaut ist (oder wenn du optionale Features aus deiner Hauptinstallation heraushalten willst).

Schneller Einstieg:

  1. Sieh dir an, was bereits geladen ist:
openclaw plugins list
  1. Installiere ein offizielles Plugin (Beispiel: Voice Call):
openclaw plugins install @openclaw/voice-call

Npm-Specs sind registry-only (Paketname + optionale exakte Version oder dist-tag). Git/URL/Datei-Specs und Semver-Ranges werden abgelehnt.

Bare-Specs und @latest bleiben auf dem stabilen Track. Wenn npm eines davon zu einer Prerelease auflöst, stoppt OpenClaw und fordert dich auf, explizit mit einem Prerelease-Tag wie @beta/@rc oder einer exakten Prerelease-Version zu bestätigen.

  1. Starte das Gateway neu, dann konfiguriere unter plugins.entries.<id>.config.

Siehe Voice Call für ein konkretes Beispiel-Plugin. Suchst du Drittanbieter-Listings? Siehe Community-Plugins.

Architektur

OpenClaws Plugin-System hat vier Ebenen:

  1. Manifest + Discovery OpenClaw findet Plugin-Kandidaten aus konfigurierten Pfaden, Workspace-Roots, globalen Extension-Roots und gebündelten Extensions. Discovery liest zuerst openclaw.plugin.json plus Paket-Metadaten.
  2. Aktivierung + Validierung Core entscheidet, ob ein entdecktes Plugin aktiviert, deaktiviert, blockiert oder für einen exklusiven Slot wie Memory ausgewählt wird.
  3. Runtime-Laden Aktivierte Plugins werden in-process via jiti geladen und registrieren Fähigkeiten in einer zentralen Registry.
  4. Oberflächen-Konsum Der Rest von OpenClaw liest die Registry, um Tools, Kanäle, Provider-Setup, Hooks, HTTP-Routen, CLI-Befehle und Services zu exponieren.

Die wichtige Design-Grenze:

  • Discovery + Config-Validierung sollte aus Manifest/Schema-Metadaten funktionieren, ohne Plugin-Code auszuführen
  • Runtime-Verhalten kommt vom register(api)-Pfad des Plugin-Moduls

Diese Trennung erlaubt OpenClaw, Config zu validieren, fehlende/deaktivierte Plugins zu erklären und UI/Schema-Hinweise zu erstellen, bevor die vollständige Runtime aktiv ist.

Ausführungsmodell

Plugins laufen in-process mit dem Gateway. Sie sind nicht gesandboxt. Ein geladenes Plugin hat die gleiche Prozess-Vertrauensgrenze wie Core-Code.

Auswirkungen:

  • ein Plugin kann Tools, Netzwerk-Handler, Hooks und Services registrieren
  • ein Plugin-Bug kann das Gateway zum Absturz bringen oder destabilisieren
  • ein bösartiges Plugin entspricht beliebiger Codeausführung innerhalb des OpenClaw-Prozesses

Verwende Allowlists und explizite Install/Load-Pfade für nicht gebündelte Plugins. Behandle Workspace-Plugins als Entwicklungscode, nicht als Produktions-Defaults.

Wichtiger Vertrauenshinweis:

  • plugins.allow vertraut Plugin-IDs, nicht der Quell-Provenienz.
  • Ein Workspace-Plugin mit derselben ID wie ein gebündeltes Plugin überschattet absichtlich die gebündelte Kopie, wenn dieses Workspace-Plugin aktiviert/in der Allowlist ist.
  • Das ist normal und nützlich für lokale Entwicklung, Patch-Tests und Hotfixes.

Verfügbare Plugins (offiziell)

  • Microsoft Teams ist seit 2026.1.15 nur als Plugin verfügbar; installiere @openclaw/msteams, wenn du Teams verwendest.
  • Memory (Core) — gebündeltes Memory-Search-Plugin (standardmäßig aktiviert über plugins.slots.memory)
  • Memory (LanceDB) — gebündeltes Langzeit-Memory-Plugin (Auto-Recall/Capture; setze plugins.slots.memory = "memory-lancedb")
  • Voice Call@openclaw/voice-call
  • Zalo Personal@openclaw/zalouser
  • Matrix@openclaw/matrix
  • Nostr@openclaw/nostr
  • Zalo@openclaw/zalo
  • Microsoft Teams@openclaw/msteams
  • Google Antigravity OAuth (Provider-Auth) — gebündelt als google-antigravity-auth (standardmäßig deaktiviert)
  • Gemini CLI OAuth (Provider-Auth) — gebündelt als google-gemini-cli-auth (standardmäßig deaktiviert)
  • Qwen OAuth (Provider-Auth) — gebündelt als qwen-portal-auth (standardmäßig deaktiviert)
  • Copilot Proxy (Provider-Auth) — lokale VS Code Copilot Proxy-Brücke; verschieden vom eingebauten github-copilot Device Login (gebündelt, standardmäßig deaktiviert)

OpenClaw-Plugins sind TypeScript-Module, die zur Runtime via jiti geladen werden. Config-Validierung führt keinen Plugin-Code aus; sie verwendet stattdessen das Plugin-Manifest und JSON Schema. Siehe Plugin-Manifest.

Plugins können registrieren:

  • Gateway-RPC-Methoden
  • Gateway-HTTP-Routen
  • Agent-Tools
  • CLI-Befehle
  • Hintergrund-Services
  • Kontext-Engines
  • Optionale Config-Validierung
  • Skills (durch Auflistung von skills-Verzeichnissen im Plugin-Manifest)
  • Auto-Reply-Befehle (ausführen ohne KI-Agent-Aufruf)

Plugins laufen in-process mit dem Gateway, behandle sie also als vertrauenswürdigen Code. Tool-Entwicklungsanleitung: Plugin Agent-Tools.

Lade-Pipeline

Beim Start führt OpenClaw grob Folgendes durch:

  1. Plugin-Root-Kandidaten entdecken
  2. openclaw.plugin.json und Paket-Metadaten lesen
  3. Unsichere Kandidaten ablehnen
  4. Plugin-Config normalisieren (plugins.enabled, allow, deny, entries, slots, load.paths)
  5. Aktivierung für jeden Kandidaten entscheiden
  6. Aktivierte Module via jiti laden
  7. register(api) aufrufen und Registrierungen in der Plugin-Registry sammeln
  8. Registry für Befehle/Runtime-Oberflächen exponieren

Die Sicherheits-Gates greifen vor der Runtime-Ausführung. Kandidaten werden blockiert, wenn der Entry das Plugin-Root verlässt, der Pfad world-writable ist oder die Pfad-Ownership für nicht gebündelte Plugins verdächtig aussieht.

Manifest-First-Verhalten

Das Manifest ist die Control-Plane-Wahrheitsquelle. OpenClaw verwendet es um:

  • das Plugin zu identifizieren
  • deklarierte Kanäle/Skills/Config-Schema zu entdecken
  • plugins.entries.<id>.config zu validieren
  • Control-UI-Labels/Platzhalter zu ergänzen
  • Install/Katalog-Metadaten anzuzeigen

Das Runtime-Modul ist der Data-Plane-Teil. Es registriert tatsächliches Verhalten wie Hooks, Tools, Befehle oder Provider-Flows.

Was der Loader cached

OpenClaw hält kurze In-Process-Caches für:

  • Discovery-Ergebnisse
  • Manifest-Registry-Daten
  • Geladene Plugin-Registries

Diese Caches reduzieren stoßweisen Start- und wiederholten Befehls-Overhead. Sie sind als kurzlebige Performance-Caches zu betrachten, nicht als Persistenz.

Runtime-Helpers

Plugins können über api.runtime auf ausgewählte Core-Helpers zugreifen. Für Telefonie-TTS:

const result = await api.runtime.tts.textToSpeechTelephony({
  text: "Hello from OpenClaw",
  cfg: api.config,
});

Hinweise:

  • Verwendet die Core-messages.tts-Konfiguration (OpenAI oder ElevenLabs).
  • Gibt PCM-Audio-Buffer + Sample-Rate zurück. Plugins müssen für Provider resamplen/enkodieren.
  • Edge TTS wird für Telefonie nicht unterstützt.

Für STT/Transkription können Plugins aufrufen:

const { text } = await api.runtime.stt.transcribeAudioFile({
  filePath: "/tmp/inbound-audio.ogg",
  cfg: api.config,
  // Optional wenn MIME nicht zuverlässig abgeleitet werden kann:
  mime: "audio/ogg",
});

Hinweise:

  • Verwendet die Core-Media-Understanding-Audio-Konfiguration (tools.media.audio) und Provider-Fallback-Reihenfolge.
  • Gibt { text: undefined } zurück, wenn keine Transkriptionsausgabe erzeugt wird (z.B. übersprungene/nicht unterstützte Eingabe).

Gateway-HTTP-Routen

Plugins können HTTP-Endpoints mit api.registerHttpRoute(...) exponieren.

api.registerHttpRoute({
  path: "/acme/webhook",
  auth: "plugin",
  match: "exact",
  handler: async (_req, res) => {
    res.statusCode = 200;
    res.end("ok");
    return true;
  },
});

Routen-Felder:

  • path: Routenpfad unter dem Gateway-HTTP-Server.
  • auth: erforderlich. Verwende "gateway" für normale Gateway-Auth oder "plugin" für plugin-verwaltete Auth/Webhook-Verifikation.
  • match: optional. "exact" (Standard) oder "prefix".
  • replaceExisting: optional. Erlaubt demselben Plugin, seine eigene bestehende Routen-Registrierung zu ersetzen.
  • handler: gibt true zurück, wenn die Route die Anfrage bearbeitet hat.

Hinweise:

  • api.registerHttpHandler(...) ist veraltet. Verwende api.registerHttpRoute(...).
  • Plugin-Routen müssen auth explizit deklarieren.
  • Exakte path + match-Konflikte werden abgelehnt, es sei denn replaceExisting: true, und ein Plugin kann nicht die Route eines anderen Plugins ersetzen.
  • Überlappende Routen mit unterschiedlichen auth-Levels werden abgelehnt. Halte exact/prefix-Fallthrough-Ketten auf demselben Auth-Level.

Plugin-SDK-Import-Pfade

Verwende SDK-Subpaths statt des monolithischen openclaw/plugin-sdk-Imports beim Entwickeln von Plugins:

  • openclaw/plugin-sdk/core für generische Plugin-APIs, Provider-Auth-Typen und gemeinsame Helpers.
  • openclaw/plugin-sdk/compat für gebündelten/internen Plugin-Code, der breitere gemeinsame Runtime-Helpers als core braucht.
  • openclaw/plugin-sdk/telegram für Telegram-Kanal-Plugins.
  • openclaw/plugin-sdk/discord für Discord-Kanal-Plugins.
  • openclaw/plugin-sdk/slack für Slack-Kanal-Plugins.
  • openclaw/plugin-sdk/signal für Signal-Kanal-Plugins.
  • openclaw/plugin-sdk/imessage für iMessage-Kanal-Plugins.
  • openclaw/plugin-sdk/whatsapp für WhatsApp-Kanal-Plugins.
  • openclaw/plugin-sdk/line für LINE-Kanal-Plugins.
  • openclaw/plugin-sdk/msteams für die gebündelte Microsoft Teams-Plugin-Oberfläche.
  • Gebündelte extension-spezifische Subpaths sind ebenfalls verfügbar: openclaw/plugin-sdk/acpx, openclaw/plugin-sdk/bluebubbles, openclaw/plugin-sdk/copilot-proxy, openclaw/plugin-sdk/device-pair, openclaw/plugin-sdk/diagnostics-otel, openclaw/plugin-sdk/diffs, openclaw/plugin-sdk/feishu, openclaw/plugin-sdk/google-gemini-cli-auth, openclaw/plugin-sdk/googlechat, openclaw/plugin-sdk/irc, openclaw/plugin-sdk/llm-task, openclaw/plugin-sdk/lobster, openclaw/plugin-sdk/matrix, openclaw/plugin-sdk/mattermost, openclaw/plugin-sdk/memory-core, openclaw/plugin-sdk/memory-lancedb, openclaw/plugin-sdk/minimax-portal-auth, openclaw/plugin-sdk/nextcloud-talk, openclaw/plugin-sdk/nostr, openclaw/plugin-sdk/open-prose, openclaw/plugin-sdk/phone-control, openclaw/plugin-sdk/qwen-portal-auth, openclaw/plugin-sdk/synology-chat, openclaw/plugin-sdk/talk-voice, openclaw/plugin-sdk/test-utils, openclaw/plugin-sdk/thread-ownership, openclaw/plugin-sdk/tlon, openclaw/plugin-sdk/twitch, openclaw/plugin-sdk/voice-call, openclaw/plugin-sdk/zalo und openclaw/plugin-sdk/zalouser.

Kompatibilitätshinweis:

  • openclaw/plugin-sdk wird für bestehende externe Plugins weiterhin unterstützt.
  • Neue und migrierte gebündelte Plugins sollten kanal- oder extension-spezifische Subpaths verwenden; verwende core für generische Oberflächen und compat nur, wenn breitere gemeinsame Helpers benötigt werden.

Read-only-Kanal-Inspektion

Wenn dein Plugin einen Kanal registriert, implementiere bevorzugt plugin.config.inspectAccount(cfg, accountId) neben resolveAccount(...).

Warum:

  • resolveAccount(...) ist der Runtime-Pfad. Es darf davon ausgehen, dass Credentials vollständig materialisiert sind, und kann bei fehlenden Secrets sofort abbrechen.
  • Read-only-Befehlspfade wie openclaw status, openclaw status --all, openclaw channels status, openclaw channels resolve und Doctor/Config-Reparatur-Flows sollten keine Runtime-Credentials materialisieren müssen, nur um die Konfiguration zu beschreiben.

Empfohlenes inspectAccount(...)-Verhalten:

  • Nur beschreibenden Account-Status zurückgeben.
  • enabled und configured bewahren.
  • Credential-Source/Status-Felder einschließen, wenn relevant, wie:
    • tokenSource, tokenStatus
    • botTokenSource, botTokenStatus
    • appTokenSource, appTokenStatus
    • signingSecretSource, signingSecretStatus
  • Du musst keine rohen Token-Werte zurückgeben, nur um Read-only-Verfügbarkeit zu melden. tokenStatus: "available" (und das passende Source-Feld) zurückzugeben reicht für Status-Befehle.
  • Verwende configured_unavailable, wenn ein Credential via SecretRef konfiguriert ist, aber im aktuellen Befehlspfad nicht verfügbar.

Damit können Read-only-Befehle “konfiguriert aber in diesem Befehlspfad nicht verfügbar” melden, statt abzustürzen oder den Account falsch als nicht konfiguriert zu melden.

Performance-Hinweis:

  • Plugin-Discovery und Manifest-Metadaten verwenden kurze In-Process-Caches, um stoßweise Start-/Reload-Arbeit zu reduzieren.
  • Setze OPENCLAW_DISABLE_PLUGIN_DISCOVERY_CACHE=1 oder OPENCLAW_DISABLE_PLUGIN_MANIFEST_CACHE=1, um diese Caches zu deaktivieren.
  • Cache-Fenster über OPENCLAW_PLUGIN_DISCOVERY_CACHE_MS und OPENCLAW_PLUGIN_MANIFEST_CACHE_MS anpassen.

Discovery & Rangfolge

OpenClaw scannt in dieser Reihenfolge:

  1. Config-Pfade
  • plugins.load.paths (Datei oder Verzeichnis)
  1. Workspace-Extensions
  • <workspace>/.openclaw/extensions/*.ts
  • <workspace>/.openclaw/extensions/*/index.ts
  1. Globale Extensions
  • ~/.openclaw/extensions/*.ts
  • ~/.openclaw/extensions/*/index.ts
  1. Gebündelte Extensions (mit OpenClaw ausgeliefert, meist standardmäßig deaktiviert)
  • <openclaw>/extensions/*

Die meisten gebündelten Plugins müssen explizit über plugins.entries.<id>.enabled oder openclaw plugins enable <id> aktiviert werden.

Standardmäßig aktivierte gebündelte Plugin-Ausnahmen:

  • device-pair
  • phone-control
  • talk-voice
  • aktives Memory-Slot-Plugin (Standard-Slot: memory-core)

Installierte Plugins sind standardmäßig aktiviert, können aber auf die gleiche Weise deaktiviert werden.

Workspace-Plugins sind standardmäßig deaktiviert, es sei denn, du aktivierst sie explizit oder setzt sie auf die Allowlist. Das ist beabsichtigt: Ein ausgechecktes Repo sollte nicht stillschweigend zu Produktions-Gateway-Code werden.

Härtungs-Hinweise:

  • Wenn plugins.allow leer ist und nicht gebündelte Plugins auffindbar sind, loggt OpenClaw eine Startup-Warnung mit Plugin-IDs und Quellen.
  • Kandidatenpfade werden vor der Discovery-Aufnahme sicherheitsgeprüft. OpenClaw blockiert Kandidaten wenn:
    • der Extension-Entry das Plugin-Root verlässt (einschließlich Symlink/Pfad-Traversal-Escapes),
    • Plugin-Root/Quellpfad world-writable ist,
    • Pfad-Ownership für nicht gebündelte Plugins verdächtig ist (POSIX-Owner ist weder aktuelle UID noch Root).
  • Geladene nicht gebündelte Plugins ohne Install/Load-Path-Provenienz geben eine Warnung aus, damit du Vertrauen pinnen (plugins.allow) oder Install-Tracking (plugins.installs) einrichten kannst.

Jedes Plugin muss eine openclaw.plugin.json-Datei in seinem Root enthalten. Wenn ein Pfad auf eine Datei zeigt, ist das Plugin-Root das Verzeichnis der Datei und muss das Manifest enthalten.

Wenn mehrere Plugins zur gleichen ID aufgelöst werden, gewinnt der erste Treffer in der obigen Reihenfolge und Kopien mit niedrigerer Priorität werden ignoriert.

Das bedeutet:

  • Workspace-Plugins überschatten absichtlich gebündelte Plugins mit derselben ID
  • plugins.allow: ["foo"] autorisiert das aktive foo-Plugin nach ID, auch wenn die aktive Kopie aus dem Workspace statt dem gebündelten Extension-Root kommt
  • wenn du striktere Provenienz-Kontrolle brauchst, verwende explizite Install/Load-Pfade und inspiziere die aufgelöste Plugin-Quelle vor dem Aktivieren

Aktivierungsregeln

Aktivierung wird nach der Discovery aufgelöst:

  • plugins.enabled: false deaktiviert alle Plugins
  • plugins.deny gewinnt immer
  • plugins.entries.<id>.enabled: false deaktiviert dieses Plugin
  • Workspace-Origin-Plugins sind standardmäßig deaktiviert
  • Allowlists schränken das aktive Set ein, wenn plugins.allow nicht leer ist
  • Allowlists sind ID-basiert, nicht quell-basiert
  • Gebündelte Plugins sind standardmäßig deaktiviert, es sei denn:
    • die gebündelte ID ist im eingebauten Standard-An-Set, oder
    • du aktivierst es explizit, oder
    • Kanal-Config aktiviert das gebündelte Kanal-Plugin implizit
  • Exklusive Slots können das ausgewählte Plugin für diesen Slot erzwingen

In aktuellem Core gehören zu den gebündelten Standard-An-IDs lokale/Provider-Helpers wie ollama, sglang, vllm, plus device-pair, phone-control und talk-voice.

Paket-Packs

Ein Plugin-Verzeichnis kann ein package.json mit openclaw.extensions enthalten:

{
  "name": "my-pack",
  "openclaw": {
    "extensions": ["./src/safety.ts", "./src/tools.ts"]
  }
}

Jeder Eintrag wird ein Plugin. Wenn das Pack mehrere Extensions listet, wird die Plugin-ID zu name/<fileBase>.

Wenn dein Plugin npm-Deps importiert, installiere sie in diesem Verzeichnis, damit node_modules verfügbar ist (npm install / pnpm install).

Sicherheitsschutz: Jeder openclaw.extensions-Eintrag muss nach Symlink-Auflösung innerhalb des Plugin-Verzeichnisses bleiben. Einträge, die das Paketverzeichnis verlassen, werden abgelehnt.

Sicherheitshinweis: openclaw plugins install installiert Plugin-Dependencies mit npm install --ignore-scripts (keine Lifecycle-Scripts). Halte Plugin-Dependency-Trees “reines JS/TS” und vermeide Pakete, die postinstall-Builds erfordern.

Kanal-Katalog-Metadaten

Kanal-Plugins können Onboarding-Metadaten über openclaw.channel und Install-Hinweise über openclaw.install bewerben. Das hält den Core-Katalog datenfrei.

Beispiel:

{
  "name": "@openclaw/nextcloud-talk",
  "openclaw": {
    "extensions": ["./index.ts"],
    "channel": {
      "id": "nextcloud-talk",
      "label": "Nextcloud Talk",
      "selectionLabel": "Nextcloud Talk (self-hosted)",
      "docsPath": "/channels/nextcloud-talk",
      "docsLabel": "nextcloud-talk",
      "blurb": "Self-hosted chat via Nextcloud Talk webhook bots.",
      "order": 65,
      "aliases": ["nc-talk", "nc"]
    },
    "install": {
      "npmSpec": "@openclaw/nextcloud-talk",
      "localPath": "extensions/nextcloud-talk",
      "defaultChoice": "npm"
    }
  }
}

OpenClaw kann auch externe Kanal-Kataloge zusammenführen (zum Beispiel einen MPM-Registry-Export). Lege eine JSON-Datei an einem der folgenden Orte ab:

  • ~/.openclaw/mpm/plugins.json
  • ~/.openclaw/mpm/catalog.json
  • ~/.openclaw/plugins/catalog.json

Oder zeige OPENCLAW_PLUGIN_CATALOG_PATHS (oder OPENCLAW_MPM_CATALOG_PATHS) auf eine oder mehrere JSON-Dateien (Komma/Semikolon/PATH-getrennt). Jede Datei sollte { "entries": [ { "name": "@scope/pkg", "openclaw": { "channel": {...}, "install": {...} } } ] } enthalten.

Plugin-IDs

Standard-Plugin-IDs:

  • Paket-Packs: package.json name
  • Standalone-Datei: Datei-Basisname (~/.../voice-call.tsvoice-call)

Wenn ein Plugin id exportiert, verwendet OpenClaw es, warnt aber, wenn es nicht mit der konfigurierten ID übereinstimmt.

Registry-Modell

Geladene Plugins mutieren nicht direkt zufällige Core-Globals. Sie registrieren sich in einer zentralen Plugin-Registry.

Die Registry verfolgt:

  • Plugin-Records (Identität, Quelle, Herkunft, Status, Diagnosen)
  • Tools
  • Legacy-Hooks und typisierte Hooks
  • Kanäle
  • Provider
  • Gateway-RPC-Handler
  • HTTP-Routen
  • CLI-Registrars
  • Hintergrund-Services
  • Plugin-eigene Befehle

Core-Features lesen dann aus dieser Registry, statt direkt mit Plugin-Modulen zu sprechen. Das hält das Laden einseitig:

  • Plugin-Modul -> Registry-Registrierung
  • Core-Runtime -> Registry-Konsum

Diese Trennung ist wichtig für die Wartbarkeit. Sie bedeutet, dass die meisten Core-Oberflächen nur einen Integrationspunkt brauchen: “Registry lesen”, nicht “jeden Plugin-Modul speziell behandeln”.

Konfiguration

{
  plugins: {
    enabled: true,
    allow: ["voice-call"],
    deny: ["untrusted-plugin"],
    load: { paths: ["~/Projects/oss/voice-call-extension"] },
    entries: {
      "voice-call": { enabled: true, config: { provider: "twilio" } },
    },
  },
}

Felder:

  • enabled: Master-Toggle (Standard: true)
  • allow: Allowlist (optional)
  • deny: Denylist (optional; Deny gewinnt)
  • load.paths: extra Plugin-Dateien/Verzeichnisse
  • slots: exklusive Slot-Selektoren wie memory und contextEngine
  • entries.<id>: pro-Plugin-Toggles + Config

Config-Änderungen erfordern einen Gateway-Neustart.

Validierungsregeln (strikt):

  • Unbekannte Plugin-IDs in entries, allow, deny oder slots sind Fehler.
  • Unbekannte channels.<id>-Keys sind Fehler, es sei denn, ein Plugin-Manifest deklariert die Kanal-ID.
  • Plugin-Config wird gegen das in openclaw.plugin.json (configSchema) eingebettete JSON Schema validiert.
  • Wenn ein Plugin deaktiviert ist, wird seine Config bewahrt und eine Warnung ausgegeben.

Deaktiviert vs. fehlend vs. ungültig

Diese Zustände sind absichtlich unterschiedlich:

  • deaktiviert: Plugin existiert, aber Aktivierungsregeln haben es ausgeschaltet
  • fehlend: Config referenziert eine Plugin-ID, die Discovery nicht gefunden hat
  • ungültig: Plugin existiert, aber seine Config passt nicht zum deklarierten Schema

OpenClaw bewahrt Config für deaktivierte Plugins, damit ein erneutes Einschalten nicht destruktiv ist.

Plugin-Slots (exklusive Kategorien)

Einige Plugin-Kategorien sind exklusiv (nur eins gleichzeitig aktiv). Verwende plugins.slots, um auszuwählen, welches Plugin den Slot besitzt:

{
  plugins: {
    slots: {
      memory: "memory-core", // oder "none" um Memory-Plugins zu deaktivieren
      contextEngine: "legacy", // oder eine Plugin-ID wie "lossless-claw"
    },
  },
}

Unterstützte exklusive Slots:

  • memory: aktives Memory-Plugin ("none" deaktiviert Memory-Plugins)
  • contextEngine: aktive Kontext-Engine-Plugin ("legacy" ist der eingebaute Standard)

Wenn mehrere Plugins kind: "memory" oder kind: "context-engine" deklarieren, wird nur das ausgewählte Plugin für diesen Slot geladen. Andere werden mit Diagnosen deaktiviert.

Kontext-Engine-Plugins

Kontext-Engine-Plugins besitzen die Session-Kontext-Orchestrierung für Ingestion, Assembly und Compaction. Registriere sie in deinem Plugin mit api.registerContextEngine(id, factory), dann wähle die aktive Engine mit plugins.slots.contextEngine.

Verwende das, wenn dein Plugin die Standard-Kontextpipeline ersetzen oder erweitern muss, statt nur Memory-Suche oder Hooks hinzuzufügen.

Control-UI (Schema + Labels)

Die Control-UI verwendet config.schema (JSON Schema + uiHints), um bessere Formulare zu rendern.

OpenClaw ergänzt uiHints zur Runtime basierend auf entdeckten Plugins:

  • Fügt pro-Plugin-Labels für plugins.entries.<id> / .enabled / .config hinzu
  • Mergt optionale plugin-bereitgestellte Config-Feld-Hinweise unter: plugins.entries.<id>.config.<field>

Wenn du möchtest, dass deine Plugin-Config-Felder gute Labels/Platzhalter anzeigen (und Secrets als sensitiv markieren), stelle uiHints neben deinem JSON Schema im Plugin-Manifest bereit.

Beispiel:

{
  "id": "my-plugin",
  "configSchema": {
    "type": "object",
    "additionalProperties": false,
    "properties": {
      "apiKey": { "type": "string" },
      "region": { "type": "string" }
    }
  },
  "uiHints": {
    "apiKey": { "label": "API Key", "sensitive": true },
    "region": { "label": "Region", "placeholder": "us-east-1" }
  }
}

CLI

openclaw plugins list
openclaw plugins info <id>
openclaw plugins install <path>                 # eine lokale Datei/Verzeichnis nach ~/.openclaw/extensions/<id> kopieren
openclaw plugins install ./extensions/voice-call # relativer Pfad ok
openclaw plugins install ./plugin.tgz           # aus lokalem Tarball installieren
openclaw plugins install ./plugin.zip           # aus lokalem Zip installieren
openclaw plugins install -l ./extensions/voice-call # linken (keine Kopie) für Entwicklung
openclaw plugins install @openclaw/voice-call # von npm installieren
openclaw plugins install @openclaw/voice-call --pin # exakten aufgelösten name@version speichern
openclaw plugins update <id>
openclaw plugins update --all
openclaw plugins enable <id>
openclaw plugins disable <id>
openclaw plugins doctor

plugins update funktioniert nur für npm-Installationen, die unter plugins.installs getrackt werden. Wenn sich gespeicherte Integritäts-Metadaten zwischen Updates ändern, warnt OpenClaw und bittet um Bestätigung (verwende globales --yes zum Umgehen von Prompts).

Plugins können auch eigene Top-Level-Befehle registrieren (Beispiel: openclaw voicecall).

Plugin-API (Überblick)

Plugins exportieren entweder:

  • Eine Funktion: (api) => { ... }
  • Ein Objekt: { id, name, configSchema, register(api) { ... } }

register(api) ist, wo Plugins Verhalten anhängen. Häufige Registrierungen umfassen:

  • registerTool
  • registerHook
  • on(...) für typisierte Lifecycle-Hooks
  • registerChannel
  • registerProvider
  • registerHttpRoute
  • registerCommand
  • registerCli
  • registerContextEngine
  • registerService

Kontext-Engine-Plugins können auch einen Runtime-eigenen Kontext-Manager registrieren:

export default function (api) {
  api.registerContextEngine("lossless-claw", () => ({
    info: { id: "lossless-claw", name: "Lossless Claw", ownsCompaction: true },
    async ingest() {
      return { ingested: true };
    },
    async assemble({ messages }) {
      return { messages, estimatedTokens: 0 };
    },
    async compact() {
      return { ok: true, compacted: false };
    },
  }));
}

Dann in der Config aktivieren:

{
  plugins: {
    slots: {
      contextEngine: "lossless-claw",
    },
  },
}

Plugin-Hooks

Plugins können Hooks zur Runtime registrieren. Das erlaubt einem Plugin, event-getriebene Automatisierung zu bündeln, ohne ein separates Hook-Pack zu installieren.

Beispiel

export default function register(api) {
  api.registerHook(
    "command:new",
    async () => {
      // Hook-Logik hier.
    },
    {
      name: "my-plugin.command-new",
      description: "Runs when /new is invoked",
    },
  );
}

Hinweise:

  • Hooks explizit über api.registerHook(...) registrieren.
  • Hook-Berechtigungsregeln gelten weiterhin (OS/Bins/Env/Config-Anforderungen).
  • Plugin-verwaltete Hooks erscheinen in openclaw hooks list mit plugin:<id>.
  • Du kannst Plugin-verwaltete Hooks nicht über openclaw hooks aktivieren/deaktivieren; aktiviere/deaktiviere stattdessen das Plugin.

Agent-Lifecycle-Hooks (api.on)

Für typisierte Runtime-Lifecycle-Hooks verwende api.on(...):

export default function register(api) {
  api.on(
    "before_prompt_build",
    (event, ctx) => {
      return {
        prependSystemContext: "Follow company style guide.",
      };
    },
    { priority: 10 },
  );
}

Wichtige Hooks für Prompt-Konstruktion:

  • before_model_resolve: läuft vor dem Session-Laden (messages sind nicht verfügbar). Verwende dies, um deterministisch modelOverride oder providerOverride zu überschreiben.
  • before_prompt_build: läuft nach dem Session-Laden (messages sind verfügbar). Verwende dies, um Prompt-Input zu formen.
  • before_agent_start: Legacy-Kompatibilitäts-Hook. Bevorzuge die zwei expliziten Hooks oben.

Core-erzwungene Hook-Policy:

  • Operatoren können Prompt-Mutations-Hooks pro Plugin über plugins.entries.<id>.hooks.allowPromptInjection: false deaktivieren.
  • Wenn deaktiviert, blockiert OpenClaw before_prompt_build und ignoriert prompt-mutierende Felder, die vom Legacy-before_agent_start zurückgegeben werden, bewahrt aber Legacy-modelOverride und providerOverride.

before_prompt_build-Ergebnisfelder:

  • prependContext: Text dem User-Prompt für diesen Run voranstellen. Am besten für turn-spezifischen oder dynamischen Content.
  • systemPrompt: vollständiger System-Prompt-Override.
  • prependSystemContext: Text dem aktuellen System-Prompt voranstellen.
  • appendSystemContext: Text an den aktuellen System-Prompt anhängen.

Prompt-Build-Reihenfolge in der eingebetteten Runtime:

  1. prependContext auf den User-Prompt anwenden.
  2. systemPrompt-Override anwenden, wenn vorhanden.
  3. prependSystemContext + aktueller System-Prompt + appendSystemContext anwenden.

Merge- und Rangfolge-Hinweise:

  • Hook-Handler laufen nach Priorität (höher zuerst).
  • Für gemergte Kontext-Felder werden Werte in Ausführungsreihenfolge konkateniert.
  • before_prompt_build-Werte werden vor Legacy-before_agent_start-Fallback-Werten angewendet.

Migrations-Anleitung:

  • Verschiebe statische Anleitung von prependContext nach prependSystemContext (oder appendSystemContext), damit Provider stabilen System-Prefix-Content cachen können.
  • Behalte prependContext für pro-Turn dynamischen Kontext, der an die Benutzernachricht gebunden bleiben soll.

Provider-Plugins (Model-Auth)

Plugins können Model-Provider registrieren, damit Benutzer OAuth- oder API-Key-Setup innerhalb von OpenClaw durchführen, Provider-Setup im Onboarding/Model-Picker darstellen und zur impliziten Provider-Discovery beitragen können.

Provider-Plugins sind die modulare Extensions-Naht für Model-Provider-Setup. Sie sind nicht nur “OAuth-Helpers” mehr.

Provider-Plugin-Lebenszyklus

Ein Provider-Plugin kann an fünf verschiedenen Phasen teilnehmen:

  1. Auth auth[].run(ctx) führt OAuth, API-Key-Erfassung, Device-Code oder benutzerdefiniertes Setup durch und gibt Auth-Profile plus optionale Config-Patches zurück.
  2. Nicht-interaktives Setup auth[].runNonInteractive(ctx) behandelt openclaw onboard --non-interactive ohne Prompts. Verwende dies, wenn der Provider benutzerdefiniertes headless Setup jenseits der eingebauten einfachen API-Key-Pfade braucht.
  3. Wizard-Integration wizard.onboarding fügt einen Eintrag zu openclaw onboard hinzu. wizard.modelPicker fügt einen Setup-Eintrag zum Model-Picker hinzu.
  4. Implizite Discovery discovery.run(ctx) kann Provider-Config automatisch während der Model-Auflösung/Auflistung beitragen.
  5. Post-Selection-Follow-Up onModelSelected(ctx) läuft, nachdem ein Modell gewählt wurde. Verwende dies für provider-spezifische Arbeit wie das Herunterladen eines lokalen Modells.

Das ist die empfohlene Aufteilung, weil diese Phasen unterschiedliche Lifecycle-Anforderungen haben:

  • Auth ist interaktiv und schreibt Credentials/Config
  • nicht-interaktives Setup ist flag/env-getrieben und darf nicht prompten
  • Wizard-Metadaten sind statisch und UI-gerichtet
  • Discovery sollte sicher, schnell und fehlertolerant sein
  • Post-Select-Hooks sind Seiteneffekte, die an das gewählte Modell gebunden sind

Provider-Auth-Vertrag

auth[].run(ctx) gibt zurück:

  • profiles: Auth-Profile zum Schreiben
  • configPatch: optionale openclaw.json-Änderungen
  • defaultModel: optionale provider/model-Referenz
  • notes: optionale benutzerseitige Hinweise

Core dann:

  1. schreibt die zurückgegebenen Auth-Profile
  2. wendet Auth-Profil-Config-Verdrahtung an
  3. mergt den Config-Patch
  4. wendet optional das Standardmodell an
  5. führt den onModelSelected-Hook des Providers aus, wenn angemessen

Das bedeutet, ein Provider-Plugin besitzt die provider-spezifische Setup-Logik, während Core den generischen Persistenz- und Config-Merge-Pfad besitzt.

Provider-nicht-interaktiver Vertrag

auth[].runNonInteractive(ctx) ist optional. Implementiere es, wenn der Provider headless Setup braucht, das nicht durch die eingebauten generischen API-Key-Flows ausgedrückt werden kann.

Der nicht-interaktive Kontext enthält:

  • die aktuelle und Basis-Config
  • geparste Onboarding-CLI-Optionen
  • Runtime-Logging/Fehler-Helpers
  • Agent/Workspace-Verzeichnisse
  • resolveApiKey(...) zum Lesen von Provider-Keys aus Flags, Env oder bestehenden Auth- Profilen unter Berücksichtigung von --secret-input-mode
  • toApiKeyCredential(...) zum Konvertieren eines aufgelösten Keys in ein Auth-Profil- Credential mit der richtigen Plaintext- vs. Secret-Ref-Speicherung

Verwende diese Oberfläche für Provider wie:

  • selbst gehostete OpenAI-kompatible Runtimes, die --custom-base-url + --custom-model-id brauchen
  • provider-spezifische nicht-interaktive Verifikation oder Config-Synthese

Prompte nicht aus runNonInteractive. Lehne fehlende Eingaben mit umsetzbaren Fehlern ab.

Provider-Wizard-Metadaten

wizard.onboarding steuert, wie der Provider im gruppierten Onboarding erscheint:

  • choiceId: Auth-Choice-Wert
  • choiceLabel: Options-Label
  • choiceHint: kurzer Hinweis
  • groupId: Gruppen-Bucket-ID
  • groupLabel: Gruppen-Label
  • groupHint: Gruppen-Hinweis
  • methodId: auszuführende Auth-Methode

wizard.modelPicker steuert, wie ein Provider als “jetzt einrichten”-Eintrag in der Modellauswahl erscheint:

  • label
  • hint
  • methodId

Wenn ein Provider mehrere Auth-Methoden hat, kann der Wizard entweder auf eine explizite Methode zeigen oder OpenClaw pro-Methode-Auswahlmöglichkeiten synthetisieren lassen.

OpenClaw validiert Provider-Wizard-Metadaten, wenn das Plugin sich registriert:

  • doppelte oder leere Auth-Methoden-IDs werden abgelehnt
  • Wizard-Metadaten werden ignoriert, wenn der Provider keine Auth-Methoden hat
  • ungültige methodId-Bindungen werden zu Warnungen herabgestuft und fallen auf die verbleibenden Auth-Methoden des Providers zurück

Provider-Discovery-Vertrag

discovery.run(ctx) gibt eines von folgenden zurück:

  • { provider }
  • { providers }
  • null

Verwende { provider } für den häufigen Fall, dass das Plugin eine Provider-ID besitzt. Verwende { providers }, wenn ein Plugin mehrere Provider-Einträge entdeckt.

Der Discovery-Kontext enthält:

  • die aktuelle Config
  • Agent/Workspace-Verzeichnisse
  • Prozess-Env
  • einen Helper zum Auflösen des Provider-API-Keys und eines discovery-sicheren API-Key-Werts

Discovery sollte:

  • schnell sein
  • best-effort sein
  • bei Fehler sicher zu überspringen sein
  • vorsichtig mit Seiteneffekten sein

Es sollte nicht von Prompts oder lang laufendem Setup abhängen.

Discovery-Reihenfolge

Provider-Discovery läuft in geordneten Phasen:

  • simple
  • profile
  • paired
  • late

Verwende:

  • simple für günstige umgebungs-only Discovery
  • profile wenn Discovery von Auth-Profilen abhängt
  • paired für Provider, die sich mit einem anderen Discovery-Schritt koordinieren müssen
  • late für teure oder lokale Netzwerk-Probing

Die meisten selbst gehosteten Provider sollten late verwenden.

Gute Provider-Plugin-Grenzen

Gut geeignet für Provider-Plugins:

  • lokale/selbst gehostete Provider mit benutzerdefinierten Setup-Flows
  • provider-spezifisches OAuth/Device-Code-Login
  • implizite Discovery lokaler Model-Server
  • Post-Selection-Seiteneffekte wie Model-Pulls

Weniger überzeugend:

  • triviale API-Key-only Provider, die sich nur durch Env-Variable, Base-URL und ein Standardmodell unterscheiden

Diese können trotzdem Plugins werden, aber der Hauptvorteil der Modularität kommt vom Extrahieren verhaltensreicher Provider zuerst.

Registriere einen Provider über api.registerProvider(...). Jeder Provider exponiert eine oder mehrere Auth-Methoden (OAuth, API-Key, Device-Code, etc.). Diese Methoden können antreiben:

  • openclaw models auth login --provider <id> [--method <id>]
  • openclaw onboard
  • Model-Picker “Custom Provider”-Setup-Einträge
  • implizite Provider-Discovery während Model-Auflösung/Auflistung

Beispiel:

api.registerProvider({
  id: "acme",
  label: "AcmeAI",
  auth: [
    {
      id: "oauth",
      label: "OAuth",
      kind: "oauth",
      run: async (ctx) => {
        // OAuth-Flow ausführen und Auth-Profile zurückgeben.
        return {
          profiles: [
            {
              profileId: "acme:default",
              credential: {
                type: "oauth",
                provider: "acme",
                access: "...",
                refresh: "...",
                expires: Date.now() + 3600 * 1000,
              },
            },
          ],
          defaultModel: "acme/opus-1",
        };
      },
    },
  ],
  wizard: {
    onboarding: {
      choiceId: "acme",
      choiceLabel: "AcmeAI",
      groupId: "acme",
      groupLabel: "AcmeAI",
      methodId: "oauth",
    },
    modelPicker: {
      label: "AcmeAI (custom)",
      hint: "Connect a self-hosted AcmeAI endpoint",
      methodId: "oauth",
    },
  },
  discovery: {
    order: "late",
    run: async () => ({
      provider: {
        baseUrl: "https://acme.example/v1",
        api: "openai-completions",
        apiKey: "${ACME_API_KEY}",
        models: [],
      },
    }),
  },
});

Hinweise:

  • run empfängt einen ProviderAuthContext mit prompter, runtime, openUrl und oauth.createVpsAwareHandlers-Helpers.
  • runNonInteractive empfängt einen ProviderAuthMethodNonInteractiveContext mit opts, resolveApiKey und toApiKeyCredential-Helpers für headless Onboarding.
  • Gib configPatch zurück, wenn du Standard-Modelle oder Provider-Config hinzufügen musst.
  • Gib defaultModel zurück, damit --set-default Agent-Defaults aktualisieren kann.
  • wizard.onboarding fügt eine Provider-Auswahl zu openclaw onboard hinzu.
  • wizard.modelPicker fügt einen “diesen Provider einrichten”-Eintrag zum Model-Picker hinzu.
  • discovery.run gibt entweder { provider } für die eigene Provider-ID des Plugins zurück oder { providers } für Multi-Provider-Discovery.
  • discovery.order steuert, wann der Provider relativ zu eingebauten Discovery-Phasen läuft: simple, profile, paired oder late.
  • onModelSelected ist der Post-Selection-Hook für provider-spezifische Follow-Up-Arbeit wie das Pullen eines lokalen Modells.

Einen Messaging-Kanal registrieren

Plugins können Kanal-Plugins registrieren, die sich wie eingebaute Kanäle verhalten (WhatsApp, Telegram, etc.). Kanal-Config liegt unter channels.<id> und wird von deinem Kanal-Plugin-Code validiert.

const myChannel = {
  id: "acmechat",
  meta: {
    id: "acmechat",
    label: "AcmeChat",
    selectionLabel: "AcmeChat (API)",
    docsPath: "/channels/acmechat",
    blurb: "demo channel plugin.",
    aliases: ["acme"],
  },
  capabilities: { chatTypes: ["direct"] },
  config: {
    listAccountIds: (cfg) => Object.keys(cfg.channels?.acmechat?.accounts ?? {}),
    resolveAccount: (cfg, accountId) =>
      cfg.channels?.acmechat?.accounts?.[accountId ?? "default"] ?? {
        accountId,
      },
  },
  outbound: {
    deliveryMode: "direct",
    sendText: async () => ({ ok: true }),
  },
};

export default function (api) {
  api.registerChannel({ plugin: myChannel });
}

Hinweise:

  • Config unter channels.<id> ablegen (nicht plugins.entries).
  • meta.label wird für Labels in CLI/UI-Listen verwendet.
  • meta.aliases fügt alternative IDs für Normalisierung und CLI-Eingaben hinzu.
  • meta.preferOver listet Kanal-IDs auf, die bei Auto-Enable übersprungen werden, wenn beide konfiguriert sind.
  • meta.detailLabel und meta.systemImage lassen UIs reichere Kanal-Labels/Icons anzeigen.

Kanal-Onboarding-Hooks

Kanal-Plugins können optionale Onboarding-Hooks auf plugin.onboarding definieren:

  • configure(ctx) ist der Basis-Setup-Flow.
  • configureInteractive(ctx) kann das interaktive Setup für sowohl konfigurierte als auch nicht konfigurierte Zustände vollständig übernehmen.
  • configureWhenConfigured(ctx) kann das Verhalten nur für bereits konfigurierte Kanäle überschreiben.

Hook-Rangfolge im Wizard:

  1. configureInteractive (wenn vorhanden)
  2. configureWhenConfigured (nur wenn Kanalstatus bereits konfiguriert)
  3. Fallback auf configure

Kontext-Details:

  • configureInteractive und configureWhenConfigured erhalten:
    • configured (true oder false)
    • label (benutzerseitiger Kanalname für Prompts)
    • plus die gemeinsamen Config/Runtime/Prompter/Options-Felder
  • Die Rückgabe von "skip" lässt Auswahl und Account-Tracking unverändert.
  • Die Rückgabe von { cfg, accountId? } wendet Config-Updates an und zeichnet die Account-Auswahl auf.

Einen neuen Messaging-Kanal schreiben (Schritt für Schritt)

Verwende dies, wenn du eine neue Chat-Oberfläche (einen “Messaging-Kanal”) willst, nicht einen Model-Provider. Model-Provider-Docs befinden sich unter /providers/*.

  1. ID + Config-Form wählen
  • Alle Kanal-Config liegt unter channels.<id>.
  • Bevorzuge channels.<id>.accounts.<accountId> für Multi-Account-Setups.
  1. Kanal-Metadaten definieren
  • meta.label, meta.selectionLabel, meta.docsPath, meta.blurb steuern CLI/UI-Listen.
  • meta.docsPath sollte auf eine Docs-Seite wie /channels/<id> zeigen.
  • meta.preferOver lässt ein Plugin einen anderen Kanal ersetzen (Auto-Enable bevorzugt es).
  • meta.detailLabel und meta.systemImage werden von UIs für Detailtext/Icons verwendet.
  1. Die erforderlichen Adapter implementieren
  • config.listAccountIds + config.resolveAccount
  • capabilities (Chat-Typen, Medien, Threads, etc.)
  • outbound.deliveryMode + outbound.sendText (für einfaches Senden)
  1. Optionale Adapter nach Bedarf hinzufügen
  • setup (Wizard), security (DM-Policy), status (Health/Diagnose)
  • gateway (Start/Stop/Login), mentions, threading, streaming
  • actions (Nachrichtenaktionen), commands (natives Befehlsverhalten)
  1. Den Kanal in deinem Plugin registrieren
  • api.registerChannel({ plugin })

Minimales Config-Beispiel:

{
  channels: {
    acmechat: {
      accounts: {
        default: { token: "ACME_TOKEN", enabled: true },
      },
    },
  },
}

Minimales Kanal-Plugin (nur Outbound):

const plugin = {
  id: "acmechat",
  meta: {
    id: "acmechat",
    label: "AcmeChat",
    selectionLabel: "AcmeChat (API)",
    docsPath: "/channels/acmechat",
    blurb: "AcmeChat messaging channel.",
    aliases: ["acme"],
  },
  capabilities: { chatTypes: ["direct"] },
  config: {
    listAccountIds: (cfg) => Object.keys(cfg.channels?.acmechat?.accounts ?? {}),
    resolveAccount: (cfg, accountId) =>
      cfg.channels?.acmechat?.accounts?.[accountId ?? "default"] ?? {
        accountId,
      },
  },
  outbound: {
    deliveryMode: "direct",
    sendText: async ({ text }) => {
      // `text` an deinen Kanal zustellen
      return { ok: true };
    },
  },
};

export default function (api) {
  api.registerChannel({ plugin });
}

Lade das Plugin (Extensions-Verzeichnis oder plugins.load.paths), starte das Gateway neu, dann konfiguriere channels.<id> in deiner Config.

Agent-Tools

Siehe die dedizierte Anleitung: Plugin Agent-Tools.

Eine Gateway-RPC-Methode registrieren

export default function (api) {
  api.registerGatewayMethod("myplugin.status", ({ respond }) => {
    respond(true, { ok: true });
  });
}

CLI-Befehle registrieren

export default function (api) {
  api.registerCli(
    ({ program }) => {
      program.command("mycmd").action(() => {
        console.log("Hello");
      });
    },
    { commands: ["mycmd"] },
  );
}

Auto-Reply-Befehle registrieren

Plugins können benutzerdefinierte Slash-Befehle registrieren, die ohne Aufruf des KI-Agents ausgeführt werden. Das ist nützlich für Toggle-Befehle, Status-Checks oder schnelle Aktionen, die keine LLM-Verarbeitung brauchen.

export default function (api) {
  api.registerCommand({
    name: "mystatus",
    description: "Show plugin status",
    handler: (ctx) => ({
      text: `Plugin is running! Channel: ${ctx.channel}`,
    }),
  });
}

Command-Handler-Kontext:

  • senderId: Die Sender-ID (wenn verfügbar)
  • channel: Der Kanal, auf dem der Befehl gesendet wurde
  • isAuthorizedSender: Ob der Sender ein autorisierter Benutzer ist
  • args: Argumente nach dem Befehl (wenn acceptsArgs: true)
  • commandBody: Der vollständige Befehlstext
  • config: Die aktuelle OpenClaw-Config

Command-Optionen:

  • name: Befehlsname (ohne führendes /)
  • nativeNames: Optionale Native-Command-Aliase für Slash/Menü-Oberflächen. Verwende default für alle nativen Provider oder provider-spezifische Keys wie discord
  • description: Hilfetext in Befehlslisten
  • acceptsArgs: Ob der Befehl Argumente akzeptiert (Standard: false). Wenn false und Argumente angegeben werden, matcht der Befehl nicht und die Nachricht fällt durch zu anderen Handlern
  • requireAuth: Ob autorisierter Sender erforderlich ist (Standard: true)
  • handler: Funktion, die { text: string } zurückgibt (kann async sein)

Beispiel mit Autorisierung und Argumenten:

api.registerCommand({
  name: "setmode",
  description: "Set plugin mode",
  acceptsArgs: true,
  requireAuth: true,
  handler: async (ctx) => {
    const mode = ctx.args?.trim() || "default";
    await saveMode(mode);
    return { text: `Mode set to: ${mode}` };
  },
});

Hinweise:

  • Plugin-Befehle werden vor eingebauten Befehlen und dem KI-Agent verarbeitet
  • Befehle werden global registriert und funktionieren über alle Kanäle
  • Befehlsnamen sind case-insensitive (/MyStatus matcht /mystatus)
  • Befehlsnamen müssen mit einem Buchstaben beginnen und dürfen nur Buchstaben, Zahlen, Bindestriche und Unterstriche enthalten
  • Reservierte Befehlsnamen (wie help, status, reset, etc.) können von Plugins nicht überschrieben werden
  • Doppelte Befehlsregistrierung über Plugins hinweg scheitert mit einem Diagnosefehler

Hintergrund-Services registrieren

export default function (api) {
  api.registerService({
    id: "my-service",
    start: () => api.logger.info("ready"),
    stop: () => api.logger.info("bye"),
  });
}

Namenskonventionen

  • Gateway-Methoden: pluginId.action (Beispiel: voicecall.status)
  • Tools: snake_case (Beispiel: voice_call)
  • CLI-Befehle: kebab oder camel, aber Konflikte mit Core-Befehlen vermeiden

Skills

Plugins können einen Skill im Repo mitliefern (skills/<name>/SKILL.md). Aktiviere ihn mit plugins.entries.<id>.enabled (oder anderen Config-Gates) und stelle sicher, dass er in deinen Workspace/verwalteten Skills-Locations vorhanden ist.

Distribution (npm)

Empfohlenes Packaging:

  • Hauptpaket: openclaw (dieses Repo)
  • Plugins: separate npm-Pakete unter @openclaw/* (Beispiel: @openclaw/voice-call)

Veröffentlichungsvertrag:

  • Plugin package.json muss openclaw.extensions mit einer oder mehreren Entry-Dateien enthalten.
  • Entry-Dateien können .js oder .ts sein (jiti lädt TS zur Runtime).
  • openclaw plugins install <npm-spec> verwendet npm pack, extrahiert nach ~/.openclaw/extensions/<id>/ und aktiviert es in der Config.
  • Config-Key-Stabilität: Scoped-Pakete werden zur unscoped ID für plugins.entries.* normalisiert.

Beispiel-Plugin: Voice Call

Dieses Repo enthält ein Voice-Call-Plugin (Twilio oder Log-Fallback):

  • Source: extensions/voice-call
  • Skill: skills/voice-call
  • CLI: openclaw voicecall start|status
  • Tool: voice_call
  • RPC: voicecall.start, voicecall.status
  • Config (Twilio): provider: "twilio" + twilio.accountSid/authToken/from (optional statusCallbackUrl, twimlUrl)
  • Config (Dev): provider: "log" (kein Netzwerk)

Siehe Voice Call und extensions/voice-call/README.md für Setup und Nutzung.

Sicherheitshinweise

Plugins laufen in-process mit dem Gateway. Behandle sie als vertrauenswürdigen Code:

  • Installiere nur Plugins, denen du vertraust.
  • Bevorzuge plugins.allow-Allowlists.
  • Denke daran, dass plugins.allow ID-basiert ist, also kann ein aktiviertes Workspace-Plugin absichtlich ein gebündeltes Plugin mit derselben ID überschatten.
  • Starte das Gateway nach Änderungen neu.

Plugins testen

Plugins können (und sollten) Tests mitliefern:

  • In-Repo-Plugins können Vitest-Tests unter src/** halten (Beispiel: src/plugins/voice-call.plugin.test.ts).
  • Separat veröffentlichte Plugins sollten ihre eigene CI (Lint/Build/Test) betreiben und validieren, dass openclaw.extensions auf den gebauten Entrypoint (dist/index.js) zeigt.