外掛(擴充)

快速入門(初次接觸外掛?)

外掛就是一個小型程式碼模組,為 OpenClaw 擴充額外功能 (指令、工具和閘道 RPC)。

多數時候,當你需要核心 OpenClaw 尚未內建的功能(或想將選用功能獨立於主安裝之外), 就會用到外掛。

快速上手:

  1. 查看已載入的外掛:
openclaw plugins list
  1. 安裝官方外掛(範例:Voice Call):
openclaw plugins install @openclaw/voice-call

Npm 規格僅限登錄處(套件名稱加選填的精確版本dist-tag)。Git/URL/檔案規格和 semver 範圍會被拒絕。

裸規格和 @latest 走穩定軌道。如果 npm 將其中任何一個解析為預發布版本, OpenClaw 會停下來請你用預發布標籤(如 @beta/@rc)或精確的預發布版本明確同意。

  1. 重新啟動閘道,然後在 plugins.entries.<id>.config 下設定。

請參閱 Voice Call 了解具體外掛範例。 尋找第三方外掛?參閱 社群外掛

架構

OpenClaw 的外掛系統有四個層級:

  1. 清單與探索 OpenClaw 從設定路徑、工作區根目錄、全域擴充根目錄和內建擴充中找到候選外掛。探索首先讀取 openclaw.plugin.json 加上套件中繼資料。
  2. 啟用與驗證 核心決定已探索的外掛是啟用、停用、阻擋或 被選為專屬插槽(如 memory)。
  3. 執行時載入 啟用的外掛透過 jiti 在程序內載入,並將功能註冊到 中央登錄處。
  4. 介面消費 OpenClaw 的其餘部分讀取登錄處來暴露工具、頻道、供應商 設定、hook、HTTP 路由、CLI 指令和服務。

重要的設計邊界:

  • 探索 + 設定驗證應基於清單/schema 中繼資料運作, 不需要執行外掛程式碼
  • 執行時行為來自外掛模組的 register(api) 路徑

這個分離讓 OpenClaw 能在完整執行環境啟動前驗證設定、解釋缺少/停用的外掛, 並建構 UI/schema 提示。

執行模型

外掛與閘道在同一程序內執行。它們不會被沙箱化。已載入的 外掛與核心程式碼有相同的程序層級信任邊界。

影響:

  • 外掛可以註冊工具、網路處理器、hook 和服務
  • 外掛的 bug 可能導致閘道崩潰或不穩定
  • 惡意外掛等同於在 OpenClaw 程序內的任意程式碼執行

對非內建外掛使用允許清單和明確的安裝/載入路徑。將 工作區外掛視為開發階段程式碼,而非正式環境預設值。

重要的信任注意事項:

  • plugins.allow 信任的是外掛 id,不是來源出處。
  • 與內建外掛同 id 的工作區外掛,在啟用/加入允許清單時會刻意遮蔽 內建版本。
  • 這是正常且有用的,用於本機開發、修補測試和緊急修復。

可用外掛(官方)

  • Microsoft Teams 自 2026.1.15 起僅以外掛形式提供;使用 Teams 請安裝 @openclaw/msteams
  • Memory (Core) — 內建記憶體搜尋外掛(透過 plugins.slots.memory 預設啟用)
  • Memory (LanceDB) — 內建長期記憶體外掛(自動召回/擷取;設定 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(供應商認證)— 以 google-antigravity-auth 內建(預設停用)
  • Gemini CLI OAuth(供應商認證)— 以 google-gemini-cli-auth 內建(預設停用)
  • Qwen OAuth(供應商認證)— 以 qwen-portal-auth 內建(預設停用)
  • Copilot Proxy(供應商認證)— 本機 VS Code Copilot Proxy 橋接;與內建 github-copilot 裝置登入不同(內建,預設停用)

OpenClaw 外掛是TypeScript 模組,透過 jiti 在執行時載入。設定 驗證不會執行外掛程式碼;它使用外掛清單和 JSON Schema。參閱 外掛清單

外掛可以註冊:

  • 閘道 RPC 方法
  • 閘道 HTTP 路由
  • 代理工具
  • CLI 指令
  • 背景服務
  • 上下文引擎
  • 選用的設定驗證
  • 技能(在外掛清單中列出 skills 目錄)
  • 自動回覆指令(不呼叫 AI 代理即可執行)

外掛與閘道在同一程序內執行,請視為可信賴的程式碼。 工具開發指南:外掛代理工具

載入管線

啟動時,OpenClaw 大致執行以下步驟:

  1. 探索候選外掛根目錄
  2. 讀取 openclaw.plugin.json 和套件中繼資料
  3. 拒絕不安全的候選
  4. 正規化外掛設定(plugins.enabledallowdenyentriesslotsload.paths
  5. 決定每個候選的啟用狀態
  6. 透過 jiti 載入啟用的模組
  7. 呼叫 register(api) 並將註冊收集到外掛登錄處
  8. 將登錄處暴露給指令/執行時介面

安全閘門在執行時執行之前觸發。當候選的入口跳脫外掛根目錄、路徑為全域可寫, 或非內建外掛的路徑擁有權可疑時,候選會被阻擋。

清單優先行為

清單是控制面的真實來源。OpenClaw 用它來:

  • 識別外掛
  • 探索已宣告的頻道/技能/設定 schema
  • 驗證 plugins.entries.<id>.config
  • 強化控制 UI 標籤/佔位符
  • 顯示安裝/目錄中繼資料

執行時模組是資料面部分。它註冊實際行為如 hook、工具、指令或供應商流程。

載入器快取的內容

OpenClaw 為以下項目保留短期程序內快取:

  • 探索結果
  • 清單登錄處資料
  • 已載入的外掛登錄處

這些快取減少突發性啟動和重複指令的開銷。可以將它們 視為短期效能快取,而非持久化儲存。

執行時輔助工具

外掛可透過 api.runtime 存取選定的核心輔助工具。用於電話語音 TTS:

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

注意事項:

  • 使用核心 messages.tts 設定(OpenAI 或 ElevenLabs)。
  • 回傳 PCM 音訊緩衝區 + 取樣率。外掛須自行為供應商重新取樣/編碼。
  • Edge TTS 不支援電話語音。

用於 STT/轉錄,外掛可呼叫:

const { text } = await api.runtime.stt.transcribeAudioFile({
  filePath: "/tmp/inbound-audio.ogg",
  cfg: api.config,
  // MIME 無法可靠推斷時的選填參數:
  mime: "audio/ogg",
});

注意事項:

  • 使用核心媒體理解音訊設定(tools.media.audio)和供應商後備順序。
  • 無轉錄輸出時回傳 { text: undefined }(例如跳過/不支援的輸入)。

閘道 HTTP 路由

外掛可透過 api.registerHttpRoute(...) 暴露 HTTP 端點。

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

路由欄位:

  • path:閘道 HTTP 伺服器下的路由路徑。
  • auth:必填。使用 "gateway" 要求一般閘道認證,或 "plugin" 由外掛管理認證/webhook 驗證。
  • match:選填。"exact"(預設)或 "prefix"
  • replaceExisting:選填。允許同一外掛替換自己已存在的路由註冊。
  • handler:路由處理請求時回傳 true

注意事項:

  • api.registerHttpHandler(...) 已過時。請使用 api.registerHttpRoute(...)
  • 外掛路由必須明確宣告 auth
  • 精確 path + match 衝突會被拒絕,除非 replaceExisting: true,且一個外掛無法替換另一個外掛的路由。
  • 不同 auth 層級的重疊路由會被拒絕。僅在相同 auth 層級上保持 exact/prefix 的穿透鏈。

外掛 SDK 匯入路徑

開發外掛時,使用 SDK 子路徑而非整體的 openclaw/plugin-sdk 匯入:

  • openclaw/plugin-sdk/core 用於通用外掛 API、供應商認證型別和共用輔助工具。
  • openclaw/plugin-sdk/compat 用於需要比 core 更廣泛共用執行時輔助工具的內建/內部外掛程式碼。
  • openclaw/plugin-sdk/telegram 用於 Telegram 頻道外掛。
  • openclaw/plugin-sdk/discord 用於 Discord 頻道外掛。
  • openclaw/plugin-sdk/slack 用於 Slack 頻道外掛。
  • openclaw/plugin-sdk/signal 用於 Signal 頻道外掛。
  • openclaw/plugin-sdk/imessage 用於 iMessage 頻道外掛。
  • openclaw/plugin-sdk/whatsapp 用於 WhatsApp 頻道外掛。
  • openclaw/plugin-sdk/line 用於 LINE 頻道外掛。
  • openclaw/plugin-sdk/msteams 用於內建 Microsoft Teams 外掛介面。
  • 內建擴充專屬子路徑也可用: openclaw/plugin-sdk/acpxopenclaw/plugin-sdk/bluebubblesopenclaw/plugin-sdk/copilot-proxyopenclaw/plugin-sdk/device-pairopenclaw/plugin-sdk/diagnostics-otelopenclaw/plugin-sdk/diffsopenclaw/plugin-sdk/feishuopenclaw/plugin-sdk/google-gemini-cli-authopenclaw/plugin-sdk/googlechatopenclaw/plugin-sdk/ircopenclaw/plugin-sdk/llm-taskopenclaw/plugin-sdk/lobsteropenclaw/plugin-sdk/matrixopenclaw/plugin-sdk/mattermostopenclaw/plugin-sdk/memory-coreopenclaw/plugin-sdk/memory-lancedbopenclaw/plugin-sdk/minimax-portal-authopenclaw/plugin-sdk/nextcloud-talkopenclaw/plugin-sdk/nostropenclaw/plugin-sdk/open-proseopenclaw/plugin-sdk/phone-controlopenclaw/plugin-sdk/qwen-portal-authopenclaw/plugin-sdk/synology-chatopenclaw/plugin-sdk/talk-voiceopenclaw/plugin-sdk/test-utilsopenclaw/plugin-sdk/thread-ownershipopenclaw/plugin-sdk/tlonopenclaw/plugin-sdk/twitchopenclaw/plugin-sdk/voice-callopenclaw/plugin-sdk/zaloopenclaw/plugin-sdk/zalouser

相容性注意事項:

  • openclaw/plugin-sdk 仍支援現有的外部外掛。
  • 新建和遷移的內建外掛應使用頻道或擴充專屬 子路徑;通用介面使用 core,僅在需要更廣泛共用輔助工具時使用 compat

唯讀頻道檢查

如果你的外掛註冊了一個頻道,建議在 resolveAccount(...) 之外也實作 plugin.config.inspectAccount(cfg, accountId)

原因:

  • resolveAccount(...) 是執行時路徑。它可以假設認證 已完全具體化,且在必要密鑰缺少時可以快速失敗。
  • 唯讀指令路徑如 openclaw statusopenclaw status --allopenclaw channels statusopenclaw channels resolve 和 doctor/設定 修復流程,不應該為了描述設定而需要具體化執行時認證。

建議的 inspectAccount(...) 行為:

  • 只回傳描述性帳號狀態。
  • 保留 enabledconfigured
  • 在相關時包含認證來源/狀態欄位,例如:
    • tokenSourcetokenStatus
    • botTokenSourcebotTokenStatus
    • appTokenSourceappTokenStatus
    • signingSecretSourcesigningSecretStatus
  • 回報唯讀可用性不需要回傳原始 token 值。 回傳 tokenStatus: "available"(及對應的來源欄位)就足夠了。
  • 當認證透過 SecretRef 設定但在目前指令路徑不可用時,使用 configured_unavailable

這讓唯讀指令能回報「已設定但在此指令路徑不可用」, 而非崩潰或錯誤地回報帳號未設定。

效能注意事項:

  • 外掛探索和清單中繼資料使用短期程序內快取來減少 突發性啟動/重載工作。
  • 設定 OPENCLAW_DISABLE_PLUGIN_DISCOVERY_CACHE=1OPENCLAW_DISABLE_PLUGIN_MANIFEST_CACHE=1 可停用快取。
  • 使用 OPENCLAW_PLUGIN_DISCOVERY_CACHE_MSOPENCLAW_PLUGIN_MANIFEST_CACHE_MS 調整快取時間窗口。

探索與優先序

OpenClaw 依序掃描:

  1. 設定路徑
  • plugins.load.paths(檔案或目錄)
  1. 工作區擴充
  • <workspace>/.openclaw/extensions/*.ts
  • <workspace>/.openclaw/extensions/*/index.ts
  1. 全域擴充
  • ~/.openclaw/extensions/*.ts
  • ~/.openclaw/extensions/*/index.ts
  1. 內建擴充(隨 OpenClaw 出貨,大多預設停用)
  • <openclaw>/extensions/*

大多數內建外掛必須透過 plugins.entries.<id>.enabledopenclaw plugins enable <id> 明確啟用。

預設啟用的內建外掛例外:

  • device-pair
  • phone-control
  • talk-voice
  • 啟用的記憶體插槽外掛(預設插槽:memory-core

已安裝的外掛預設為啟用,但可以用相同方式停用。

工作區外掛預設為停用,除非你明確啟用或加入允許清單。這是故意的: 一個 checkout 的 repo 不應該默默變成正式環境的閘道程式碼。

強化注意事項:

  • 如果 plugins.allow 為空且非內建外掛可被探索到,OpenClaw 在啟動時記錄警告並列出外掛 id 和來源。
  • 候選路徑在探索准入前會進行安全檢查。OpenClaw 在以下情況阻擋候選:
    • 擴充入口解析後跳脫外掛根目錄(包括符號連結/路徑穿越跳脫),
    • 外掛根目錄/來源路徑為全域可寫,
    • 非內建外掛的路徑擁有權可疑(POSIX 擁有者既非當前 uid 也非 root)。
  • 已載入但沒有安裝/載入路徑出處的非內建外掛會發出警告,讓你可以固定信任(plugins.allow)或安裝追蹤(plugins.installs)。

每個外掛必須在其根目錄包含 openclaw.plugin.json 檔案。如果路徑 指向一個檔案,外掛根目錄是該檔案的目錄且必須包含清單。

如果多個外掛解析到相同 id,上述順序中第一個匹配的生效, 較低優先序的副本被忽略。

這意味著:

  • 工作區外掛會刻意遮蔽相同 id 的內建外掛
  • plugins.allow: ["foo"] 按 id 授權啟用的 foo 外掛,即使 啟用的副本來自工作區而非內建擴充根目錄
  • 如果需要更嚴格的出處控制,使用明確的安裝/載入路徑並 在啟用前檢查解析的外掛來源

啟用規則

啟用在探索之後解析:

  • plugins.enabled: false 停用所有外掛
  • plugins.deny 永遠優先
  • plugins.entries.<id>.enabled: false 停用該外掛
  • 工作區來源的外掛預設停用
  • plugins.allow 非空時,允許清單限制啟用集合
  • 允許清單基於 id,不基於來源
  • 內建外掛預設停用,除非:
    • 內建 id 在內建預設啟用集合中,或
    • 你明確啟用它,或
    • 頻道設定隱含啟用了內建頻道外掛
  • 專屬插槽可以強制啟用該插槽選定的外掛

在目前的核心中,內建預設啟用的 id 包括本機/供應商輔助如 ollamasglangvllm,以及 device-pairphone-controltalk-voice

套件包

外掛目錄可以包含帶有 openclaw.extensionspackage.json

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

每個項目成為一個外掛。如果套件包列出多個擴充,外掛 id 變為 name/<fileBase>

如果你的外掛匯入 npm 相依套件,在該目錄安裝它們以使 node_modules 可用(npm install / pnpm install)。

安全防護:每個 openclaw.extensions 項目在符號連結解析後必須留在外掛 目錄內。跳脫套件目錄的項目會被拒絕。

安全注意事項:openclaw plugins installnpm install --ignore-scripts(無生命週期腳本)安裝外掛相依。保持外掛相依 樹為「純 JS/TS」,避免需要 postinstall 建構的套件。

頻道目錄中繼資料

頻道外掛可透過 openclaw.channel 宣傳引導中繼資料, 透過 openclaw.install 宣傳安裝提示。這讓核心目錄保持無資料。

範例:

{
  "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 也可以合併外部頻道目錄(例如 MPM 登錄處匯出)。將 JSON 檔案放在以下位置之一:

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

或將 OPENCLAW_PLUGIN_CATALOG_PATHS(或 OPENCLAW_MPM_CATALOG_PATHS)指向 一或多個 JSON 檔案(逗號/分號/PATH 分隔)。每個檔案應包含 { "entries": [ { "name": "@scope/pkg", "openclaw": { "channel": {...}, "install": {...} } } ] }

外掛 ID

預設外掛 id:

  • 套件包:package.jsonname
  • 獨立檔案:檔案基本名稱(~/.../voice-call.tsvoice-call

如果外掛匯出 id,OpenClaw 會使用它,但在不匹配設定 id 時發出警告。

登錄處模型

已載入的外掛不會直接修改隨機的核心全域變數。它們註冊到 中央外掛登錄處。

登錄處追蹤:

  • 外掛記錄(身分、來源、來源、狀態、診斷)
  • 工具
  • 舊版 hook 和型別化 hook
  • 頻道
  • 供應商
  • 閘道 RPC 處理器
  • HTTP 路由
  • CLI 註冊器
  • 背景服務
  • 外掛擁有的指令

核心功能接著從該登錄處讀取,而不是直接與外掛模組溝通。 這讓載入保持單向:

  • 外掛模組 -> 登錄處註冊
  • 核心執行環境 -> 登錄處消費

這種分離對可維護性很重要。它意味著大多數核心介面只需要 一個整合點:「讀取登錄處」,而不是「為每個外掛模組特殊處理」。

設定

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

欄位:

  • enabled:總開關(預設:true)
  • allow:允許清單(選填)
  • deny:拒絕清單(選填;deny 優先)
  • load.paths:額外的外掛檔案/目錄
  • slots:專屬插槽選擇器,如 memorycontextEngine
  • entries.<id>:每個外掛的開關與設定

設定變更需要重啟閘道

驗證規則(嚴格):

  • entriesallowdenyslots 中的未知外掛 id 是錯誤
  • 未知的 channels.<id> 鍵值是錯誤,除非外掛清單宣告了該頻道 id。
  • 外掛設定使用 openclaw.plugin.jsonconfigSchema)中嵌入的 JSON Schema 驗證。
  • 如果外掛被停用,其設定會保留並發出警告

停用 vs 缺少 vs 無效

這些狀態是刻意區分的:

  • 停用:外掛存在,但啟用規則將其關閉
  • 缺少:設定引用了探索未找到的外掛 id
  • 無效:外掛存在,但其設定不符合宣告的 schema

OpenClaw 保留停用外掛的設定,以便重新啟用時不會破壞性。

外掛插槽(專屬類別)

部分外掛類別是專屬的(同時只能有一個啟用)。使用 plugins.slots 選擇由哪個外掛擁有該插槽:

{
  plugins: {
    slots: {
      memory: "memory-core", // 或 "none" 停用記憶體外掛
      contextEngine: "legacy", // 或外掛 id 如 "lossless-claw"
    },
  },
}

支援的專屬插槽:

  • memory:啟用的記憶體外掛("none" 停用記憶體外掛)
  • contextEngine:啟用的上下文引擎外掛("legacy" 為內建預設值)

如果多個外掛宣告 kind: "memory"kind: "context-engine",只有 被選定的外掛為該插槽載入。其他外掛會被停用並附帶診斷資訊。

上下文引擎外掛

上下文引擎外掛負責工作階段上下文的攝入、組裝和壓縮協調。 從你的外掛中使用 api.registerContextEngine(id, factory) 註冊, 然後用 plugins.slots.contextEngine 選擇啟用的引擎。

當你的外掛需要替換或擴展預設的上下文管線(而非僅僅添加記憶體搜尋或 hook)時使用。

控制 UI(schema + 標籤)

控制 UI 使用 config.schema(JSON Schema + uiHints)來渲染更好的表單。

OpenClaw 基於已探索的外掛在執行時強化 uiHints

  • plugins.entries.<id> / .enabled / .config 加入每外掛標籤
  • plugins.entries.<id>.config.<field> 下合併外掛提供的選填設定欄位提示

如果你希望外掛設定欄位顯示好的標籤/佔位符(並將密鑰標記為敏感), 請在外掛清單中隨 JSON Schema 提供 uiHints

範例:

{
  "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>                 # 將本機檔案/目錄複製到 ~/.openclaw/extensions/<id>
openclaw plugins install ./extensions/voice-call # 相對路徑可用
openclaw plugins install ./plugin.tgz           # 從本機 tarball 安裝
openclaw plugins install ./plugin.zip           # 從本機 zip 安裝
openclaw plugins install -l ./extensions/voice-call # 連結(不複製)用於開發
openclaw plugins install @openclaw/voice-call # 從 npm 安裝
openclaw plugins install @openclaw/voice-call --pin # 儲存精確解析的 name@version
openclaw plugins update <id>
openclaw plugins update --all
openclaw plugins enable <id>
openclaw plugins disable <id>
openclaw plugins doctor

plugins update 僅適用於 plugins.installs 中追蹤的 npm 安裝。 如果儲存的完整性中繼資料在更新間有變更,OpenClaw 會警告並要求確認(使用全域 --yes 繞過提示)。

外掛也可以註冊自己的頂層指令(範例:openclaw voicecall)。

外掛 API(概觀)

外掛匯出以下之一:

  • 函式:(api) => { ... }
  • 物件:{ id, name, configSchema, register(api) { ... } }

register(api) 是外掛附加行為的地方。常見的註冊包括:

  • registerTool
  • registerHook
  • on(...) 用於型別化生命週期 hook
  • registerChannel
  • registerProvider
  • registerHttpRoute
  • registerCommand
  • registerCli
  • registerContextEngine
  • registerService

上下文引擎外掛也可以註冊執行時擁有的上下文管理器:

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 };
    },
  }));
}

然後在設定中啟用:

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

外掛 Hook

外掛可以在執行時註冊 hook。這讓外掛能打包事件驅動的 自動化,不需要另外安裝 hook 包。

範例

export default function register(api) {
  api.registerHook(
    "command:new",
    async () => {
      // Hook 邏輯寫在這裡。
    },
    {
      name: "my-plugin.command-new",
      description: "Runs when /new is invoked",
    },
  );
}

注意事項:

  • 透過 api.registerHook(...) 明確註冊 hook。
  • Hook 資格規則仍然適用(作業系統/二進位檔案/環境變數/設定要求)。
  • 外掛管理的 hook 在 openclaw hooks list 中以 plugin:<id> 顯示。
  • 無法透過 openclaw hooks 啟用/停用外掛管理的 hook;請改為啟用/停用外掛。

代理生命週期 hook(api.on

用於型別化的執行時生命週期 hook,使用 api.on(...)

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

重要的提示詞建構 hook:

  • before_model_resolve:在工作階段載入前執行(messages 不可用)。用於確定性地覆寫 modelOverrideproviderOverride
  • before_prompt_build:在工作階段載入後執行(messages 可用)。用於塑造提示詞輸入。
  • before_agent_start:舊版相容性 hook。建議使用上述兩個明確的 hook。

核心強制的 hook 策略:

  • 營運者可透過 plugins.entries.<id>.hooks.allowPromptInjection: false 為每個外掛停用提示詞變更 hook。
  • 停用時,OpenClaw 阻擋 before_prompt_build 並忽略舊版 before_agent_start 回傳的提示詞變更欄位,同時保留舊版的 modelOverrideproviderOverride

before_prompt_build 結果欄位:

  • prependContext:為此次執行在使用者提示詞前插入文字。適合與回合相關或動態的內容。
  • systemPrompt:完整的系統提示詞覆寫。
  • prependSystemContext:在當前系統提示詞前插入文字。
  • appendSystemContext:在當前系統提示詞後附加文字。

內嵌執行環境中的提示詞建構順序:

  1. prependContext 套用到使用者提示詞。
  2. 提供 systemPrompt 覆寫時套用。
  3. 套用 prependSystemContext + 當前系統提示詞 + appendSystemContext

合併與優先序注意事項:

  • Hook 處理器按優先序執行(數值越高越先)。
  • 合併的上下文欄位按執行順序串接。
  • before_prompt_build 的值在舊版 before_agent_start 後備值之前套用。

遷移指引:

  • 將靜態指引從 prependContext 移到 prependSystemContext(或 appendSystemContext),讓供應商能快取穩定的系統前綴內容。
  • prependContext 保留給應與使用者訊息綁定的每回合動態上下文。

供應商外掛(模型認證)

外掛可以註冊模型供應商,讓使用者能在 OpenClaw 內進行 OAuth 或 API 金鑰 設定、在引導/模型選擇器中顯示供應商設定,並 貢獻隱含的供應商探索。

供應商外掛是模型供應商設定的模組化擴展接縫。它們 不再只是「OAuth 輔助工具」。

供應商外掛生命週期

供應商外掛可以參與五個不同的階段:

  1. 認證 auth[].run(ctx) 執行 OAuth、API 金鑰擷取、裝置代碼或自訂 設定,並回傳認證設定檔加上選填的設定補丁。
  2. 非互動式設定 auth[].runNonInteractive(ctx) 處理 openclaw onboard --non-interactive 而不需要提示。當供應商需要超出內建簡易 API 金鑰流程的自訂無頭設定時使用。
  3. 精靈整合 wizard.onboardingopenclaw onboard 中加入項目。 wizard.modelPicker 在模型選擇器中加入設定項目。
  4. 隱含探索 discovery.run(ctx) 可在模型解析/列出時自動貢獻供應商設定。
  5. 選擇後跟進 onModelSelected(ctx) 在模型被選擇後執行。用於供應商 特定的工作,如下載本機模型。

建議這樣拆分是因為這些階段有不同的生命週期需求:

  • 認證是互動式的,會寫入認證/設定
  • 非互動式設定由旗標/環境驅動,不可提示
  • 精靈中繼資料是靜態的且面向 UI
  • 探索應安全、快速且容錯
  • 選擇後 hook 是綁定到選定模型的副作用

供應商認證合約

auth[].run(ctx) 回傳:

  • profiles:要寫入的認證設定檔
  • configPatch:選填的 openclaw.json 變更
  • defaultModel:選填的 provider/model 參考
  • notes:選填的使用者面向說明

核心接著:

  1. 寫入回傳的認證設定檔
  2. 套用認證設定檔的設定配線
  3. 合併設定補丁
  4. 選填地套用預設模型
  5. 適當時執行供應商的 onModelSelected hook

這意味著供應商外掛負責供應商特定的設定邏輯,而核心 負責通用的持久化和設定合併路徑。

供應商非互動式合約

auth[].runNonInteractive(ctx) 是選填的。當供應商 需要無法透過內建通用 API 金鑰流程表達的無頭設定時實作。

非互動式上下文包含:

  • 當前和基礎設定
  • 解析的引導 CLI 選項
  • 執行時日誌/錯誤輔助工具
  • 代理/工作區目錄
  • resolveApiKey(...) 從旗標、環境或現有認證設定檔讀取供應商金鑰, 同時遵循 --secret-input-mode
  • toApiKeyCredential(...) 將解析的金鑰轉換為具有正確明文 vs secret-ref 儲存的認證設定檔認證

使用此介面的場景包括:

  • 需要 --custom-base-url + --custom-model-id 的自架 OpenAI 相容執行環境
  • 供應商特定的非互動式驗證或設定合成

不要在 runNonInteractive 中提示。缺少輸入時以可操作的 錯誤拒絕。

供應商精靈中繼資料

wizard.onboarding 控制供應商如何出現在分組引導中:

  • choiceId:認證選擇值
  • choiceLabel:選項標籤
  • choiceHint:簡短提示
  • groupId:分組桶 id
  • groupLabel:分組標籤
  • groupHint:分組提示
  • methodId:要執行的認證方法

wizard.modelPicker 控制供應商如何作為「立即設定」 項目出現在模型選擇中:

  • label
  • hint
  • methodId

當供應商有多個認證方法時,精靈可以指向一個 明確的方法,或讓 OpenClaw 合成每方法的選項。

OpenClaw 在外掛註冊時驗證供應商精靈中繼資料:

  • 重複或空白的認證方法 id 會被拒絕
  • 供應商沒有認證方法時精靈中繼資料被忽略
  • 無效的 methodId 綁定降級為警告並後備到供應商的其餘認證方法

供應商探索合約

discovery.run(ctx) 回傳以下之一:

  • { provider }
  • { providers }
  • null

常見情況下外掛擁有一個供應商 id 時使用 { provider }。 外掛探索多個供應商項目時使用 { providers }

探索上下文包含:

  • 當前設定
  • 代理/工作區目錄
  • 程序環境
  • 解析供應商 API 金鑰和探索安全 API 金鑰值的輔助工具

探索應該:

  • 快速
  • 盡力而為
  • 失敗時可安全跳過
  • 注意副作用

不應依賴提示或長時間運行的設定。

探索順序

供應商探索按有序階段執行:

  • simple
  • profile
  • paired
  • late

使用方式:

  • simple 用於僅需環境的廉價探索
  • profile 用於依賴認證設定檔的探索
  • paired 用於需要與另一個探索步驟協調的供應商
  • late 用於昂貴的或本機網路探測

大多數自架供應商應使用 late

好的供應商外掛邊界

適合作為供應商外掛的:

  • 具有自訂設定流程的本機/自架供應商
  • 供應商特定的 OAuth/裝置代碼登入
  • 本機模型伺服器的隱含探索
  • 選擇後的副作用如模型拉取

較不適合的:

  • 僅差異在環境變數、base URL 和一個預設模型的簡易 API 金鑰供應商

這些仍然可以成為外掛,但主要的模組化回報來自 先提取行為豐富的供應商。

透過 api.registerProvider(...) 註冊供應商。每個供應商暴露一或 多個認證方法(OAuth、API 金鑰、裝置代碼等)。這些方法可以驅動:

  • openclaw models auth login --provider <id> [--method <id>]
  • openclaw onboard
  • 模型選擇器的「自訂供應商」設定項目
  • 模型解析/列出時的隱含供應商探索

範例:

api.registerProvider({
  id: "acme",
  label: "AcmeAI",
  auth: [
    {
      id: "oauth",
      label: "OAuth",
      kind: "oauth",
      run: async (ctx) => {
        // 執行 OAuth 流程並回傳認證設定檔。
        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: [],
      },
    }),
  },
});

注意事項:

  • run 接收 ProviderAuthContext,含 prompterruntimeopenUrloauth.createVpsAwareHandlers 輔助工具。
  • runNonInteractive 接收 ProviderAuthMethodNonInteractiveContext, 含 optsresolveApiKeytoApiKeyCredential 輔助工具用於 無頭引導。
  • 需要加入預設模型或供應商設定時回傳 configPatch
  • 回傳 defaultModel--set-default 可以更新代理預設值。
  • wizard.onboardingopenclaw onboard 中加入供應商選項。
  • wizard.modelPicker 在模型選擇器中加入「設定此供應商」項目。
  • discovery.run 回傳 { provider }(外掛自身的供應商 id)或 { providers }(多供應商探索)。
  • discovery.order 控制供應商相對於內建探索階段的執行時機: simpleprofilepairedlate
  • onModelSelected 是供應商特定後續工作(如拉取本機模型)的選擇後 hook。

註冊訊息頻道

外掛可以註冊頻道外掛,行為如同內建頻道 (WhatsApp、Telegram 等)。頻道設定位於 channels.<id> 下, 由你的頻道外掛程式碼驗證。

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 });
}

注意事項:

  • 設定放在 channels.<id> 下(不是 plugins.entries)。
  • meta.label 用於 CLI/UI 清單中的標籤。
  • meta.aliases 為正規化和 CLI 輸入加入替代 id。
  • meta.preferOver 列出兩者都設定時要跳過自動啟用的頻道 id。
  • meta.detailLabelmeta.systemImage 讓 UI 顯示更豐富的頻道標籤/圖示。

頻道引導 Hook

頻道外掛可在 plugin.onboarding 上定義選填的引導 hook:

  • configure(ctx) 是基礎設定流程。
  • configureInteractive(ctx) 可完全掌控已設定和未設定狀態的互動式設定。
  • configureWhenConfigured(ctx) 僅在頻道已設定時覆寫行為。

精靈中的 hook 優先序:

  1. configureInteractive(如存在)
  2. configureWhenConfigured(僅在頻道狀態為已設定時)
  3. 後備至 configure

上下文細節:

  • configureInteractiveconfigureWhenConfigured 接收:
    • configuredtruefalse
    • label(提示使用的使用者面向頻道名稱)
    • 加上共用的 config/runtime/prompter/options 欄位
  • 回傳 "skip" 讓選擇和帳號追蹤保持不變。
  • 回傳 { cfg, accountId? } 套用設定更新並記錄帳號選擇。

撰寫新的訊息頻道(步驟說明)

當你想要一個新的聊天介面(「訊息頻道」)而非模型供應商時使用。 模型供應商文件在 /providers/* 下。

  1. 選擇 id 和設定結構
  • 所有頻道設定位於 channels.<id> 下。
  • 多帳號設定建議使用 channels.<id>.accounts.<accountId>
  1. 定義頻道中繼資料
  • meta.labelmeta.selectionLabelmeta.docsPathmeta.blurb 控制 CLI/UI 清單。
  • meta.docsPath 應指向文件頁面如 /channels/<id>
  • meta.preferOver 讓外掛取代另一個頻道(自動啟用優先選擇它)。
  • meta.detailLabelmeta.systemImage 用於 UI 的詳細文字/圖示。
  1. 實作必要的轉接器
  • config.listAccountIds + config.resolveAccount
  • capabilities(聊天類型、媒體、執行緒等)
  • outbound.deliveryMode + outbound.sendText(用於基礎傳送)
  1. 按需加入選填轉接器
  • setup(精靈)、security(私訊策略)、status(健康/診斷)
  • gateway(啟動/停止/登入)、mentionsthreadingstreaming
  • actions(訊息操作)、commands(原生指令行為)
  1. 在你的外掛中註冊頻道
  • api.registerChannel({ plugin })

精簡設定範例:

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

精簡頻道外掛(僅發送):

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` 傳送到你的頻道
      return { ok: true };
    },
  },
};

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

載入外掛(extensions 目錄或 plugins.load.paths),重啟閘道, 然後在設定中設定 channels.<id>

代理工具

請參閱專門指南:外掛代理工具

註冊閘道 RPC 方法

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

註冊 CLI 指令

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

註冊自動回覆指令

外掛可以註冊不呼叫 AI 代理即可執行的自訂斜線指令。 適合開關指令、狀態檢查或不需要 LLM 處理的快速操作。

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

指令處理器上下文:

  • senderId:發送者的 ID(如有)
  • channel:指令傳送的頻道
  • isAuthorizedSender:發送者是否為授權使用者
  • args:指令後傳入的引數(如 acceptsArgs: true
  • commandBody:完整的指令文字
  • config:當前 OpenClaw 設定

指令選項:

  • name:指令名稱(不含前導 /
  • nativeNames:選填的原生指令別名,用於斜線/選單介面。使用 default 針對所有原生供應商,或供應商特定鍵值如 discord
  • description:指令清單中顯示的說明文字
  • acceptsArgs:指令是否接受引數(預設:false)。若為 false 且提供了引數,指令不會匹配,訊息穿透到其他處理器
  • requireAuth:是否需要授權發送者(預設:true)
  • handler:回傳 { text: string } 的函式(可為非同步)

含授權和引數的範例:

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}` };
  },
});

注意事項:

  • 外掛指令在內建指令和 AI 代理之前處理
  • 指令全域註冊,在所有頻道都能使用
  • 指令名稱不分大小寫(/MyStatus 匹配 /mystatus
  • 指令名稱必須以字母開頭,只包含字母、數字、連字號和底線
  • 保留的指令名稱(如 helpstatusreset 等)不能被外掛覆寫
  • 跨外掛的重複指令註冊會以診斷錯誤失敗

註冊背景服務

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

命名慣例

  • 閘道方法:pluginId.action(範例:voicecall.status
  • 工具:snake_case(範例:voice_call
  • CLI 指令:kebab 或 camel,但避免與核心指令衝突

技能

外掛可以在 repo 中附帶技能(skills/<name>/SKILL.md)。 透過 plugins.entries.<id>.enabled(或其他設定閘控)啟用,並確保 它存在於你的工作區/受管理技能位置。

發布(npm)

建議的打包方式:

  • 主套件:openclaw(此 repo)
  • 外掛:@openclaw/* 下的獨立 npm 套件(範例:@openclaw/voice-call

發布合約:

  • 外掛的 package.json 必須包含 openclaw.extensions 和一或多個入口檔案。
  • 入口檔案可以是 .js.ts(jiti 在執行時載入 TS)。
  • openclaw plugins install <npm-spec> 使用 npm pack,解壓到 ~/.openclaw/extensions/<id>/,並在設定中啟用。
  • 設定鍵值穩定性:有範圍的套件在 plugins.entries.* 中正規化為無範圍 id。

範例外掛:Voice Call

此 repo 包含語音通話外掛(Twilio 或 log 後備):

  • 原始碼:extensions/voice-call
  • 技能:skills/voice-call
  • CLI:openclaw voicecall start|status
  • 工具:voice_call
  • RPC:voicecall.startvoicecall.status
  • 設定(twilio):provider: "twilio" + twilio.accountSid/authToken/from(選填 statusCallbackUrltwimlUrl
  • 設定(dev):provider: "log"(不走網路)

請參閱 Voice Callextensions/voice-call/README.md 了解設定與使用方式。

安全注意事項

外掛與閘道在同一程序內執行。請視為可信賴的程式碼:

  • 僅安裝你信賴的外掛。
  • 優先使用 plugins.allow 允許清單。
  • 記住 plugins.allow 是基於 id 的,因此啟用的工作區外掛可以 刻意遮蔽相同 id 的內建外掛。
  • 變更後重新啟動閘道。

測試外掛

外掛可以(且應該)附帶測試:

  • repo 內的外掛可以在 src/** 下保留 Vitest 測試(範例:src/plugins/voice-call.plugin.test.ts)。
  • 獨立發布的外掛應自行運行 CI(lint/build/test),並驗證 openclaw.extensions 指向已建構的入口(dist/index.js)。