外掛(擴充)
快速入門(初次接觸外掛?)
外掛就是一個小型程式碼模組,為 OpenClaw 擴充額外功能 (指令、工具和閘道 RPC)。
多數時候,當你需要核心 OpenClaw 尚未內建的功能(或想將選用功能獨立於主安裝之外), 就會用到外掛。
快速上手:
- 查看已載入的外掛:
openclaw plugins list
- 安裝官方外掛(範例:Voice Call):
openclaw plugins install @openclaw/voice-call
Npm 規格僅限登錄處(套件名稱加選填的精確版本或 dist-tag)。Git/URL/檔案規格和 semver 範圍會被拒絕。
裸規格和 @latest 走穩定軌道。如果 npm 將其中任何一個解析為預發布版本,
OpenClaw 會停下來請你用預發布標籤(如 @beta/@rc)或精確的預發布版本明確同意。
- 重新啟動閘道,然後在
plugins.entries.<id>.config下設定。
請參閱 Voice Call 了解具體外掛範例。 尋找第三方外掛?參閱 社群外掛。
架構
OpenClaw 的外掛系統有四個層級:
- 清單與探索
OpenClaw 從設定路徑、工作區根目錄、全域擴充根目錄和內建擴充中找到候選外掛。探索首先讀取
openclaw.plugin.json加上套件中繼資料。 - 啟用與驗證 核心決定已探索的外掛是啟用、停用、阻擋或 被選為專屬插槽(如 memory)。
- 執行時載入 啟用的外掛透過 jiti 在程序內載入,並將功能註冊到 中央登錄處。
- 介面消費 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 大致執行以下步驟:
- 探索候選外掛根目錄
- 讀取
openclaw.plugin.json和套件中繼資料 - 拒絕不安全的候選
- 正規化外掛設定(
plugins.enabled、allow、deny、entries、slots、load.paths) - 決定每個候選的啟用狀態
- 透過 jiti 載入啟用的模組
- 呼叫
register(api)並將註冊收集到外掛登錄處 - 將登錄處暴露給指令/執行時介面
安全閘門在執行時執行之前觸發。當候選的入口跳脫外掛根目錄、路徑為全域可寫, 或非內建外掛的路徑擁有權可疑時,候選會被阻擋。
清單優先行為
清單是控制面的真實來源。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/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和openclaw/plugin-sdk/zalouser。
相容性注意事項:
openclaw/plugin-sdk仍支援現有的外部外掛。- 新建和遷移的內建外掛應使用頻道或擴充專屬
子路徑;通用介面使用
core,僅在需要更廣泛共用輔助工具時使用compat。
唯讀頻道檢查
如果你的外掛註冊了一個頻道,建議在 resolveAccount(...) 之外也實作
plugin.config.inspectAccount(cfg, accountId)。
原因:
resolveAccount(...)是執行時路徑。它可以假設認證 已完全具體化,且在必要密鑰缺少時可以快速失敗。- 唯讀指令路徑如
openclaw status、openclaw status --all、openclaw channels status、openclaw channels resolve和 doctor/設定 修復流程,不應該為了描述設定而需要具體化執行時認證。
建議的 inspectAccount(...) 行為:
- 只回傳描述性帳號狀態。
- 保留
enabled和configured。 - 在相關時包含認證來源/狀態欄位,例如:
tokenSource、tokenStatusbotTokenSource、botTokenStatusappTokenSource、appTokenStatussigningSecretSource、signingSecretStatus
- 回報唯讀可用性不需要回傳原始 token 值。
回傳
tokenStatus: "available"(及對應的來源欄位)就足夠了。 - 當認證透過 SecretRef 設定但在目前指令路徑不可用時,使用
configured_unavailable。
這讓唯讀指令能回報「已設定但在此指令路徑不可用」, 而非崩潰或錯誤地回報帳號未設定。
效能注意事項:
- 外掛探索和清單中繼資料使用短期程序內快取來減少 突發性啟動/重載工作。
- 設定
OPENCLAW_DISABLE_PLUGIN_DISCOVERY_CACHE=1或OPENCLAW_DISABLE_PLUGIN_MANIFEST_CACHE=1可停用快取。 - 使用
OPENCLAW_PLUGIN_DISCOVERY_CACHE_MS和OPENCLAW_PLUGIN_MANIFEST_CACHE_MS調整快取時間窗口。
探索與優先序
OpenClaw 依序掃描:
- 設定路徑
plugins.load.paths(檔案或目錄)
- 工作區擴充
<workspace>/.openclaw/extensions/*.ts<workspace>/.openclaw/extensions/*/index.ts
- 全域擴充
~/.openclaw/extensions/*.ts~/.openclaw/extensions/*/index.ts
- 內建擴充(隨 OpenClaw 出貨,大多預設停用)
<openclaw>/extensions/*
大多數內建外掛必須透過
plugins.entries.<id>.enabled 或 openclaw plugins enable <id> 明確啟用。
預設啟用的內建外掛例外:
device-pairphone-controltalk-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 包括本機/供應商輔助如
ollama、sglang、vllm,以及 device-pair、phone-control 和
talk-voice。
套件包
外掛目錄可以包含帶有 openclaw.extensions 的 package.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 install 以
npm 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.json的name - 獨立檔案:檔案基本名稱(
~/.../voice-call.ts→voice-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:專屬插槽選擇器,如memory和contextEngineentries.<id>:每個外掛的開關與設定
設定變更需要重啟閘道。
驗證規則(嚴格):
entries、allow、deny或slots中的未知外掛 id 是錯誤。- 未知的
channels.<id>鍵值是錯誤,除非外掛清單宣告了該頻道 id。 - 外掛設定使用
openclaw.plugin.json(configSchema)中嵌入的 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) 是外掛附加行為的地方。常見的註冊包括:
registerToolregisterHookon(...)用於型別化生命週期 hookregisterChannelregisterProviderregisterHttpRouteregisterCommandregisterCliregisterContextEngineregisterService
上下文引擎外掛也可以註冊執行時擁有的上下文管理器:
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不可用)。用於確定性地覆寫modelOverride或providerOverride。before_prompt_build:在工作階段載入後執行(messages可用)。用於塑造提示詞輸入。before_agent_start:舊版相容性 hook。建議使用上述兩個明確的 hook。
核心強制的 hook 策略:
- 營運者可透過
plugins.entries.<id>.hooks.allowPromptInjection: false為每個外掛停用提示詞變更 hook。 - 停用時,OpenClaw 阻擋
before_prompt_build並忽略舊版before_agent_start回傳的提示詞變更欄位,同時保留舊版的modelOverride和providerOverride。
before_prompt_build 結果欄位:
prependContext:為此次執行在使用者提示詞前插入文字。適合與回合相關或動態的內容。systemPrompt:完整的系統提示詞覆寫。prependSystemContext:在當前系統提示詞前插入文字。appendSystemContext:在當前系統提示詞後附加文字。
內嵌執行環境中的提示詞建構順序:
- 將
prependContext套用到使用者提示詞。 - 提供
systemPrompt覆寫時套用。 - 套用
prependSystemContext + 當前系統提示詞 + appendSystemContext。
合併與優先序注意事項:
- Hook 處理器按優先序執行(數值越高越先)。
- 合併的上下文欄位按執行順序串接。
before_prompt_build的值在舊版before_agent_start後備值之前套用。
遷移指引:
- 將靜態指引從
prependContext移到prependSystemContext(或appendSystemContext),讓供應商能快取穩定的系統前綴內容。 - 將
prependContext保留給應與使用者訊息綁定的每回合動態上下文。
供應商外掛(模型認證)
外掛可以註冊模型供應商,讓使用者能在 OpenClaw 內進行 OAuth 或 API 金鑰 設定、在引導/模型選擇器中顯示供應商設定,並 貢獻隱含的供應商探索。
供應商外掛是模型供應商設定的模組化擴展接縫。它們 不再只是「OAuth 輔助工具」。
供應商外掛生命週期
供應商外掛可以參與五個不同的階段:
- 認證
auth[].run(ctx)執行 OAuth、API 金鑰擷取、裝置代碼或自訂 設定,並回傳認證設定檔加上選填的設定補丁。 - 非互動式設定
auth[].runNonInteractive(ctx)處理openclaw onboard --non-interactive而不需要提示。當供應商需要超出內建簡易 API 金鑰流程的自訂無頭設定時使用。 - 精靈整合
wizard.onboarding在openclaw onboard中加入項目。wizard.modelPicker在模型選擇器中加入設定項目。 - 隱含探索
discovery.run(ctx)可在模型解析/列出時自動貢獻供應商設定。 - 選擇後跟進
onModelSelected(ctx)在模型被選擇後執行。用於供應商 特定的工作,如下載本機模型。
建議這樣拆分是因為這些階段有不同的生命週期需求:
- 認證是互動式的,會寫入認證/設定
- 非互動式設定由旗標/環境驅動,不可提示
- 精靈中繼資料是靜態的且面向 UI
- 探索應安全、快速且容錯
- 選擇後 hook 是綁定到選定模型的副作用
供應商認證合約
auth[].run(ctx) 回傳:
profiles:要寫入的認證設定檔configPatch:選填的openclaw.json變更defaultModel:選填的provider/model參考notes:選填的使用者面向說明
核心接著:
- 寫入回傳的認證設定檔
- 套用認證設定檔的設定配線
- 合併設定補丁
- 選填地套用預設模型
- 適當時執行供應商的
onModelSelectedhook
這意味著供應商外掛負責供應商特定的設定邏輯,而核心 負責通用的持久化和設定合併路徑。
供應商非互動式合約
auth[].runNonInteractive(ctx) 是選填的。當供應商
需要無法透過內建通用 API 金鑰流程表達的無頭設定時實作。
非互動式上下文包含:
- 當前和基礎設定
- 解析的引導 CLI 選項
- 執行時日誌/錯誤輔助工具
- 代理/工作區目錄
resolveApiKey(...)從旗標、環境或現有認證設定檔讀取供應商金鑰, 同時遵循--secret-input-modetoApiKeyCredential(...)將解析的金鑰轉換為具有正確明文 vs secret-ref 儲存的認證設定檔認證
使用此介面的場景包括:
- 需要
--custom-base-url+--custom-model-id的自架 OpenAI 相容執行環境 - 供應商特定的非互動式驗證或設定合成
不要在 runNonInteractive 中提示。缺少輸入時以可操作的
錯誤拒絕。
供應商精靈中繼資料
wizard.onboarding 控制供應商如何出現在分組引導中:
choiceId:認證選擇值choiceLabel:選項標籤choiceHint:簡短提示groupId:分組桶 idgroupLabel:分組標籤groupHint:分組提示methodId:要執行的認證方法
wizard.modelPicker 控制供應商如何作為「立即設定」
項目出現在模型選擇中:
labelhintmethodId
當供應商有多個認證方法時,精靈可以指向一個 明確的方法,或讓 OpenClaw 合成每方法的選項。
OpenClaw 在外掛註冊時驗證供應商精靈中繼資料:
- 重複或空白的認證方法 id 會被拒絕
- 供應商沒有認證方法時精靈中繼資料被忽略
- 無效的
methodId綁定降級為警告並後備到供應商的其餘認證方法
供應商探索合約
discovery.run(ctx) 回傳以下之一:
{ provider }{ providers }null
常見情況下外掛擁有一個供應商 id 時使用 { provider }。
外掛探索多個供應商項目時使用 { providers }。
探索上下文包含:
- 當前設定
- 代理/工作區目錄
- 程序環境
- 解析供應商 API 金鑰和探索安全 API 金鑰值的輔助工具
探索應該:
- 快速
- 盡力而為
- 失敗時可安全跳過
- 注意副作用
不應依賴提示或長時間運行的設定。
探索順序
供應商探索按有序階段執行:
simpleprofilepairedlate
使用方式:
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,含prompter、runtime、openUrl和oauth.createVpsAwareHandlers輔助工具。runNonInteractive接收ProviderAuthMethodNonInteractiveContext, 含opts、resolveApiKey和toApiKeyCredential輔助工具用於 無頭引導。- 需要加入預設模型或供應商設定時回傳
configPatch。 - 回傳
defaultModel讓--set-default可以更新代理預設值。 wizard.onboarding在openclaw onboard中加入供應商選項。wizard.modelPicker在模型選擇器中加入「設定此供應商」項目。discovery.run回傳{ provider }(外掛自身的供應商 id)或{ providers }(多供應商探索)。discovery.order控制供應商相對於內建探索階段的執行時機:simple、profile、paired或late。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.detailLabel和meta.systemImage讓 UI 顯示更豐富的頻道標籤/圖示。
頻道引導 Hook
頻道外掛可在 plugin.onboarding 上定義選填的引導 hook:
configure(ctx)是基礎設定流程。configureInteractive(ctx)可完全掌控已設定和未設定狀態的互動式設定。configureWhenConfigured(ctx)僅在頻道已設定時覆寫行為。
精靈中的 hook 優先序:
configureInteractive(如存在)configureWhenConfigured(僅在頻道狀態為已設定時)- 後備至
configure
上下文細節:
configureInteractive和configureWhenConfigured接收:configured(true或false)label(提示使用的使用者面向頻道名稱)- 加上共用的 config/runtime/prompter/options 欄位
- 回傳
"skip"讓選擇和帳號追蹤保持不變。 - 回傳
{ cfg, accountId? }套用設定更新並記錄帳號選擇。
撰寫新的訊息頻道(步驟說明)
當你想要一個新的聊天介面(「訊息頻道」)而非模型供應商時使用。
模型供應商文件在 /providers/* 下。
- 選擇 id 和設定結構
- 所有頻道設定位於
channels.<id>下。 - 多帳號設定建議使用
channels.<id>.accounts.<accountId>。
- 定義頻道中繼資料
meta.label、meta.selectionLabel、meta.docsPath、meta.blurb控制 CLI/UI 清單。meta.docsPath應指向文件頁面如/channels/<id>。meta.preferOver讓外掛取代另一個頻道(自動啟用優先選擇它)。meta.detailLabel和meta.systemImage用於 UI 的詳細文字/圖示。
- 實作必要的轉接器
config.listAccountIds+config.resolveAccountcapabilities(聊天類型、媒體、執行緒等)outbound.deliveryMode+outbound.sendText(用於基礎傳送)
- 按需加入選填轉接器
setup(精靈)、security(私訊策略)、status(健康/診斷)gateway(啟動/停止/登入)、mentions、threading、streamingactions(訊息操作)、commands(原生指令行為)
- 在你的外掛中註冊頻道
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針對所有原生供應商,或供應商特定鍵值如discorddescription:指令清單中顯示的說明文字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) - 指令名稱必須以字母開頭,只包含字母、數字、連字號和底線
- 保留的指令名稱(如
help、status、reset等)不能被外掛覆寫 - 跨外掛的重複指令註冊會以診斷錯誤失敗
註冊背景服務
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.start、voicecall.status - 設定(twilio):
provider: "twilio"+twilio.accountSid/authToken/from(選填statusCallbackUrl、twimlUrl) - 設定(dev):
provider: "log"(不走網路)
請參閱 Voice Call 和 extensions/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)。