Plugins
التفاصيل الداخلية لمعمارية Plugin
للاطلاع على نموذج الإمكانات العام، وأشكال Plugin، وعقود الملكية/التنفيذ، راجع بنية Plugin. هذه الصفحة هي المرجع للآليات الداخلية: مسار التحميل، والسجل، وخطافات وقت التشغيل، ومسارات HTTP الخاصة بـ Gateway، ومسارات الاستيراد، وجداول المخططات.
مسار التحميل
عند بدء التشغيل، ينفذ OpenClaw تقريبًا ما يلي:
- اكتشاف جذور Plugin المرشحة
- قراءة بيانات التعريف للحزم وملفات manifest الأصلية أو المتوافقة
- رفض المرشحين غير الآمنين
- تطبيع إعدادات Plugin (
plugins.enabled,allow,deny,entries,slots,load.paths) - تحديد التمكين لكل مرشح
- تحميل الوحدات الأصلية المفعلة: تستخدم الوحدات المضمنة المبنية محملًا أصليًا؛ ويستخدم مصدر TypeScript المحلي التابع لجهة خارجية بديل Jiti الطارئ
- استدعاء خطافات
register(api)الأصلية وجمع التسجيلات في سجل Plugin - إتاحة السجل للأوامر/أسطح وقت التشغيل
تحدث بوابات السلامة قبل تنفيذ وقت التشغيل. يُحظر المرشحون عندما يخرج المدخل من جذر Plugin، أو يكون المسار قابلاً للكتابة من الجميع، أو تبدو ملكية المسار مريبة بالنسبة إلى plugins غير المضمنة.
يبقى المرشحون المحظورون مرتبطين بمعرّف Plugin الخاص بهم للتشخيصات. إذا كانت الإعدادات لا تزال تشير إلى ذلك المعرّف، فسيبلغ التحقق عن Plugin بوصفه موجودًا لكنه محظور ويشير إلى تحذير سلامة المسار بدلًا من التعامل مع إدخال الإعدادات كإدخال قديم.
سلوك manifest أولًا
ملف manifest هو مصدر الحقيقة لمستوى التحكم. يستخدمه OpenClaw من أجل:
- تعريف Plugin
- اكتشاف القنوات/Skills/مخطط الإعدادات أو إمكانات الحزمة المعلنة
- التحقق من
plugins.entries.<id>.config - إثراء تسميات/عناصر نائبة في واجهة Control UI
- عرض بيانات تعريف التثبيت/الفهرس
- الحفاظ على واصفات تنشيط وإعداد رخيصة من دون تحميل وقت تشغيل Plugin
بالنسبة إلى plugins الأصلية، تكون وحدة وقت التشغيل هي جزء مستوى البيانات. فهي تسجل السلوك الفعلي مثل الخطافات، أو الأدوات، أو الأوامر، أو تدفقات المزوّدين.
تبقى كتل activation وsetup الاختيارية في manifest على مستوى التحكم.
إنها واصفات بيانات تعريف فقط لتخطيط التنشيط واكتشاف الإعداد؛
ولا تستبدل تسجيل وقت التشغيل، أو register(...)، أو setupEntry.
يستخدم مستهلكو التنشيط الحي الأوائل الآن تلميحات أوامر manifest والقنوات والمزوّدين
لتضييق تحميل Plugin قبل التشكيل الأوسع للسجل:
- يضيق تحميل CLI إلى plugins التي تملك الأمر الأساسي المطلوب
- يضيق حل إعداد القناة/Plugin إلى plugins التي تملك معرّف القناة المطلوب
- يضيق حل إعداد/وقت تشغيل المزوّد الصريح إلى plugins التي تملك معرّف المزوّد المطلوب
- يستخدم تخطيط بدء تشغيل Gateway
activation.onStartupلعمليات استيراد بدء التشغيل الصريحة وإلغاء الاشتراك في بدء التشغيل؛ لا يتم تحميل plugins التي لا تحتوي على بيانات تعريف بدء التشغيل إلا عبر مشغلات تنشيط أضيق
لا تزال عمليات التحميل المسبق لوقت التشغيل وقت الطلب التي تطلب النطاق الواسع all تشتق
مجموعة معرفات Plugin فعالة وصريحة من الإعدادات، وتخطيط بدء التشغيل، والقنوات
المعدة، والفتحات، وقواعد التمكين التلقائي. إذا كانت تلك المجموعة المشتقة فارغة، فسيحمّل OpenClaw
سجل وقت تشغيل فارغًا بدلًا من التوسيع إلى كل Plugin قابل للاكتشاف.
يكشف مخطط التنشيط كلًا من API للمعرفات فقط للمتصلين الحاليين وAPI
للمخطط للتشخيصات الجديدة. تبلغ إدخالات المخطط عن سبب اختيار Plugin،
مع فصل تلميحات مخطط activation.* الصريحة عن احتياطي ملكية manifest
مثل providers، وchannels، وcommandAliases، وsetup.providers،
وcontracts.tools، والخطافات. هذا الفصل في الأسباب هو حد التوافق:
تستمر بيانات تعريف Plugin الحالية في العمل، بينما يمكن للكود الجديد اكتشاف التلميحات الواسعة
أو سلوك الاحتياطي من دون تغيير دلالات تحميل وقت التشغيل.
يفضل اكتشاف الإعداد الآن المعرفات المملوكة للواصف مثل setup.providers و
setup.cliBackends لتضييق plugins المرشحة قبل الرجوع إلى
setup-api بالنسبة إلى plugins التي لا تزال تحتاج إلى خطافات وقت تشغيل وقت الإعداد. تستخدم
قوائم إعداد المزوّد providerAuthChoices من manifest، وخيارات الإعداد المشتقة من الواصف،
وبيانات تعريف فهرس التثبيت من دون تحميل وقت تشغيل المزوّد. إن
setup.requiresRuntime: false الصريح هو حد للواصف فقط؛ ويحافظ حذف
requiresRuntime على احتياطي setup-api القديم للتوافق. إذا ادعى أكثر
من Plugin مكتشف ملكية معرّف مزوّد إعداد أو خلفية CLI مطبع نفسه، يرفض بحث الإعداد
المالك الغامض بدلًا من الاعتماد على
ترتيب الاكتشاف. عندما يتم تنفيذ وقت تشغيل الإعداد، تبلغ تشخيصات السجل عن
الانحراف بين setup.providers / setup.cliBackends والمزوّدين أو خلفيات CLI
المسجلة بواسطة setup-api من دون حظر plugins القديمة.
حد ذاكرة Plugin المؤقتة
لا يخزن OpenClaw نتائج اكتشاف Plugin أو بيانات سجل manifest المباشرة خلف نوافذ زمنية حائطية. يجب أن تصبح عمليات التثبيت، وتعديلات manifest، وتغييرات مسار التحميل مرئية عند قراءة بيانات التعريف الصريحة التالية أو إعادة بناء اللقطة التالية. قد يحتفظ محلل ملف manifest بذاكرة مؤقتة محدودة لتوقيع الملف، مربوطة بمسار manifest المفتوح، وinode، والحجم، والطوابع الزمنية؛ لا تتجنب تلك الذاكرة المؤقتة إلا إعادة تحليل البايتات غير المتغيرة، ويجب ألا تخزن إجابات الاكتشاف، أو السجل، أو المالك، أو السياسة مؤقتًا.
المسار السريع الآمن لبيانات التعريف هو ملكية الكائن الصريحة، وليس ذاكرة مؤقتة مخفية.
ينبغي أن تمرر المسارات الساخنة لبدء تشغيل Gateway قيمة PluginMetadataSnapshot الحالية، أو
PluginLookUpTable المشتقة، أو سجل manifest صريحًا عبر سلسلة الاستدعاء. يمكن
لتحقق الإعدادات، والتمكين التلقائي عند بدء التشغيل، وتهيئة Plugin، واختيار المزوّد
إعادة استخدام تلك الكائنات طالما أنها تمثل الإعدادات الحالية ومخزون
Plugin. لا يزال بحث الإعداد يعيد بناء بيانات تعريف manifest عند الطلب
ما لم يتلق مسار الإعداد المحدد سجل manifest صريحًا؛ أبقِ ذلك
كاحتياطي للمسار البارد بدلًا من إضافة ذاكرات بحث مؤقتة مخفية. عندما يتغير الإدخال،
أعد بناء اللقطة واستبدلها بدلًا من تعديلها أو الاحتفاظ
بنسخ تاريخية.
ينبغي إعادة حساب العروض فوق سجل Plugin النشط ومساعدات تهيئة القنوات المضمنة
من السجل/الجذر الحالي. الخرائط قصيرة العمر مقبولة
داخل استدعاء واحد لإزالة تكرار العمل أو منع إعادة الدخول؛ يجب ألا تتحول إلى ذاكرات
بيانات تعريف مؤقتة للعملية.
بالنسبة إلى تحميل Plugin، تكون طبقة الذاكرة المؤقتة المستمرة هي تحميل وقت التشغيل. قد تعيد استخدام حالة المحمل عندما يتم فعليًا تحميل الكود أو عناصر التثبيت، مثل:
PluginLoaderCacheStateوسجلات وقت التشغيل النشطة المتوافقة- ذاكرات jiti/module المؤقتة وذاكرات محمل السطح العام المؤقتة المستخدمة لتجنب استيراد سطح وقت التشغيل نفسه مرارًا
- ذاكرات نظام الملفات المؤقتة لعناصر Plugin المثبتة
- خرائط قصيرة العمر لكل استدعاء لتطبيع المسار أو حل التكرارات
تلك الذاكرات المؤقتة هي تفاصيل تنفيذ لمستوى البيانات. يجب ألا تجيب عن أسئلة مستوى التحكم مثل "أي Plugin يملك هذا المزوّد؟" ما لم يكن المتصل قد طلب تحميل وقت التشغيل عمدًا.
لا تضف ذاكرات مؤقتة مستمرة أو زمنية حائطية لـ:
- نتائج الاكتشاف
- سجلات manifest المباشرة
- سجلات manifest المعاد بناؤها من فهرس Plugin المثبت
- بحث مالك المزوّد، أو قمع النموذج، أو سياسة المزوّد، أو بيانات تعريف العنصر العام
- أي إجابة أخرى مشتقة من manifest حيث ينبغي أن يظهر manifest متغير، أو فهرس مثبت، أو مسار تحميل عند قراءة بيانات التعريف التالية
يعيد المتصلون الذين يبنون بيانات تعريف manifest من فهرس Plugin المثبت والمستمر بناء ذلك السجل عند الطلب. الفهرس المثبت هو حالة متينة على مستوى المصدر؛ وليس ذاكرة بيانات تعريف مؤقتة مخفية داخل العملية.
نموذج السجل
لا تعدل plugins المحملة عموميات عشوائية في النواة مباشرة. بل تسجل في سجل Plugin مركزي.
يتتبع السجل:
- سجلات Plugin (الهوية، المصدر، الأصل، الحالة، التشخيصات)
- الأدوات
- الخطافات القديمة والخطافات النوعية
- القنوات
- المزوّدين
- معالجات RPC الخاصة بـ Gateway
- مسارات HTTP
- مسجلي CLI
- خدمات الخلفية
- الأوامر المملوكة لـ Plugin
تقرأ ميزات النواة بعد ذلك من ذلك السجل بدلًا من التحدث إلى وحدات Plugin مباشرة. يحافظ هذا على التحميل باتجاه واحد:
- وحدة Plugin -> تسجيل في السجل
- وقت تشغيل النواة -> استهلاك السجل
هذا الفصل مهم لقابلية الصيانة. فهو يعني أن معظم أسطح النواة لا تحتاج إلا إلى نقطة تكامل واحدة: "قراءة السجل"، لا "معالجة كل وحدة Plugin كحالة خاصة".
استدعاءات ربط المحادثة
يمكن لـ plugins التي تربط محادثة أن تتفاعل عند حل موافقة.
استخدم api.onConversationBindingResolved(...) لتلقي استدعاء بعد
الموافقة على طلب ربط أو رفضه:
export default {
id: "my-plugin",
register(api) {
api.onConversationBindingResolved(async (event) => {
if (event.status === "approved") {
// A binding now exists for this plugin + conversation.
console.log(event.binding?.conversationId);
return;
}
// The request was denied; clear any local pending state.
console.log(event.request.conversation.conversationId);
});
},
};
حقول حمولة الاستدعاء:
status:"approved"أو"denied"decision:"allow-once"، أو"allow-always"، أو"deny"binding: الربط المحلول للطلبات الموافق عليهاrequest: ملخص الطلب الأصلي، وتلميح الفصل، ومعرّف المرسل، و بيانات تعريف المحادثة
هذا الاستدعاء للإشعار فقط. لا يغير من يُسمح له بربط محادثة، ويعمل بعد انتهاء معالجة الموافقة في النواة.
خطافات وقت تشغيل المزوّد
تحتوي plugins الخاصة بالمزوّدين على ثلاث طبقات:
- بيانات تعريف manifest للبحث الرخيص قبل وقت التشغيل:
setup.providers[].envVars، والتوافق المتقادمproviderAuthEnvVars، وproviderAuthAliases، وproviderAuthChoices، وchannelEnvVars. - خطافات وقت الإعدادات:
catalog(القديمdiscovery) بالإضافة إلىapplyConfigDefaults. - خطافات وقت التشغيل: أكثر من 40 خطافًا اختياريًا تغطي المصادقة، وحل النماذج، وتغليف التدفق، ومستويات التفكير، وسياسة الإعادة، ونقاط نهاية الاستخدام. راجع القائمة الكاملة ضمن ترتيب الخطافات والاستخدام.
لا يزال OpenClaw يملك حلقة الوكيل العامة، وتجاوز الفشل، ومعالجة النصوص، و سياسة الأدوات. هذه الخطافات هي سطح الامتداد للسلوك الخاص بالمزوّد من دون الحاجة إلى نقل استدلال مخصص بالكامل.
استخدم setup.providers[].envVars في manifest عندما يملك المزوّد
بيانات اعتماد قائمة على البيئة ينبغي أن تراها مسارات المصادقة/الحالة/منتقي النماذج العامة من دون
تحميل وقت تشغيل Plugin. لا يزال providerAuthEnvVars المتقادم يُقرأ بواسطة
محول التوافق أثناء نافذة الإهمال، وتتلقى plugins غير المضمنة
التي تستخدمه تشخيص manifest. استخدم providerAuthAliases في manifest
عندما ينبغي لمعرّف مزوّد واحد إعادة استخدام متغيرات بيئة معرّف مزوّد آخر، وملفات تعريف المصادقة،
والمصادقة المدعومة بالإعدادات، وخيار تهيئة مفتاح API. استخدم
providerAuthChoices في manifest عندما ينبغي أن تعرف أسطح CLI الخاصة بالتهيئة/اختيار المصادقة
معرّف خيار المزوّد، وتسميات المجموعات، وأسلاك المصادقة البسيطة ذات العلم الواحد من دون
تحميل وقت تشغيل المزوّد. أبقِ
envVars الخاصة بوقت تشغيل المزوّد للتلميحات الموجهة للمشغل مثل تسميات التهيئة أو متغيرات إعداد
client-id/client-secret الخاصة بـ OAuth.
استخدم channelEnvVars في manifest عندما تحتوي قناة على مصادقة أو إعداد مدفوع بالبيئة ينبغي
أن تراه احتياطيات shell-env العامة، أو فحوصات الإعدادات/الحالة، أو مطالبات الإعداد
من دون تحميل وقت تشغيل القناة.
ترتيب الخطافات والاستخدام
بالنسبة إلى plugins الخاصة بالنماذج/المزوّدين، يستدعي OpenClaw الخطافات بهذا الترتيب التقريبي.
عمود "متى تستخدم" هو دليل القرار السريع.
لا تُدرج هنا حقول المزوّد المخصصة للتوافق فقط التي لم يعد OpenClaw يستدعيها، مثل
ProviderPlugin.capabilities وsuppressBuiltInModel، وذلك عن قصد.
| # | Hook | ما يفعله | متى يُستخدم |
|---|---|---|---|
| 1 | catalog |
نشر إعدادات المزوّد في models.providers أثناء إنشاء models.json |
عندما يمتلك المزوّد كتالوجًا أو قيمًا افتراضية لعنوان URL الأساسي |
| 2 | applyConfigDefaults |
تطبيق القيم الافتراضية العامة للإعدادات التي يملكها المزوّد أثناء تجسيد الإعدادات | عندما تعتمد القيم الافتراضية على وضع المصادقة أو البيئة أو دلالات عائلة نماذج المزوّد |
| -- | (البحث المدمج عن النموذج) | يحاول OpenClaw مسار السجل/الكتالوج العادي أولًا | (ليس hook خاصًا بـ plugin) |
| 3 | normalizeModelId |
تطبيع الأسماء البديلة القديمة أو التجريبية لمعرّفات النماذج قبل البحث | عندما يملك المزوّد تنظيف الأسماء البديلة قبل حل النموذج القياسي |
| 4 | normalizeTransport |
تطبيع api / baseUrl الخاصة بعائلة المزوّد قبل تجميع النموذج العام |
عندما يملك المزوّد تنظيف النقل لمعرّفات المزوّدين المخصصة ضمن عائلة النقل نفسها |
| 5 | normalizeConfig |
تطبيع models.providers.<id> قبل حل وقت التشغيل/المزوّد |
عندما يحتاج المزوّد إلى تنظيف إعدادات يجب أن يبقى مع Plugin؛ وتدعم مساعدات عائلة Google المضمّنة أيضًا إدخالات إعدادات Google المدعومة |
| 6 | applyNativeStreamingUsageCompat |
تطبيق عمليات إعادة كتابة توافق استخدام البث الأصلي على مزوّدي الإعدادات | عندما يحتاج المزوّد إلى إصلاحات بيانات وصفية لاستخدام البث الأصلي تقودها نقطة النهاية |
| 7 | resolveConfigApiKey |
حل مصادقة علامة البيئة لمزوّدي الإعدادات قبل تحميل مصادقة وقت التشغيل | عندما يملك المزوّد حل مفتاح API لعلامة البيئة؛ ويملك amazon-bedrock أيضًا محللًا مدمجًا لعلامة بيئة AWS هنا |
| 8 | resolveSyntheticAuth |
إظهار مصادقة محلية/مستضافة ذاتيًا أو مدعومة بالإعدادات دون حفظ نص عادي | عندما يستطيع المزوّد العمل بعلامة اعتماد اصطناعية/محلية |
| 9 | resolveExternalAuthProfiles |
تراكب ملفات تعريف المصادقة الخارجية التي يملكها المزوّد؛ القيمة الافتراضية لـ persistence هي runtime-only لبيانات اعتماد CLI/التطبيق |
عندما يعيد المزوّد استخدام بيانات اعتماد مصادقة خارجية دون حفظ رموز تحديث منسوخة؛ صرّح عن contracts.externalAuthProviders في البيان |
| 10 | shouldDeferSyntheticProfileAuth |
خفض أولوية عناصر نائبة لملفات تعريف اصطناعية مخزنة خلف مصادقة مدعومة بالبيئة/الإعدادات | عندما يخزّن المزوّد ملفات تعريف اصطناعية نائبة لا ينبغي أن تفوز بالأسبقية |
| 11 | resolveDynamicModel |
مزامنة احتياطية لمعرّفات النماذج التي يملكها المزوّد وغير الموجودة بعد في السجل المحلي | عندما يقبل المزوّد معرّفات نماذج علوية عشوائية |
| 12 | prepareDynamicModel |
إحماء غير متزامن، ثم يعمل resolveDynamicModel مرة أخرى |
عندما يحتاج المزوّد إلى بيانات وصفية من الشبكة قبل حل المعرّفات غير المعروفة |
| 13 | normalizeResolvedModel |
إعادة الكتابة النهائية قبل أن يستخدم المشغّل المضمّن النموذج المحلول | عندما يحتاج المزوّد إلى إعادة كتابة النقل لكنه لا يزال يستخدم نقلًا أساسيًا |
| 14 | contributeResolvedModelCompat |
المساهمة بعلامات توافق لنماذج البائعين خلف نقل متوافق آخر | عندما يتعرّف المزوّد على نماذجه الخاصة على عمليات نقل الوكيل دون الاستيلاء على المزوّد |
| 15 | normalizeToolSchemas |
تطبيع مخططات الأدوات قبل أن يراها المشغّل المضمّن | عندما يحتاج المزوّد إلى تنظيف مخطط عائلة النقل |
| 16 | inspectToolSchemas |
إظهار تشخيصات المخططات التي يملكها المزوّد بعد التطبيع | عندما يريد المزوّد تحذيرات كلمات مفتاحية دون تعليم النواة قواعد خاصة بالمزوّد |
| 17 | resolveReasoningOutputMode |
اختيار عقد مخرجات الاستدلال الأصلي مقابل الموسوم | عندما يحتاج المزوّد إلى استدلال موسوم/مخرج نهائي بدلًا من الحقول الأصلية |
| 18 | prepareExtraParams |
تطبيع معاملات الطلب قبل مغلفات خيارات البث العامة | عندما يحتاج المزوّد إلى معاملات طلب افتراضية أو تنظيف معاملات لكل مزوّد |
| 19 | createStreamFn |
استبدال مسار البث العادي بالكامل بنقل مخصص | عندما يحتاج المزوّد إلى بروتوكول سلكي مخصص، وليس مجرد مغلّف |
| 20 | wrapStreamFn |
مغلّف بث بعد تطبيق المغلّفات العامة | عندما يحتاج المزوّد إلى مغلّفات توافق لرؤوس الطلب/الجسم/النموذج دون نقل مخصص |
| 21 | resolveTransportTurnState |
إرفاق رؤوس نقل أصلية لكل دور أو بيانات وصفية | عندما يريد المزوّد أن ترسل عمليات النقل العامة هوية الدور الأصلية للمزوّد |
| 22 | resolveWebSocketSessionPolicy |
إرفاق رؤوس WebSocket أصلية أو سياسة تهدئة للجلسة | عندما يريد المزوّد أن تضبط عمليات نقل WS العامة رؤوس الجلسة أو سياسة الرجوع |
| 23 | formatApiKey |
منسّق ملف تعريف المصادقة: يصبح الملف الشخصي المخزّن سلسلة apiKey في وقت التشغيل |
عندما يخزّن المزوّد بيانات وصفية إضافية للمصادقة ويحتاج إلى شكل رمز تشغيل مخصص |
| 24 | refreshOAuth |
تجاوز تحديث OAuth لنقاط نهاية تحديث مخصصة أو سياسة فشل التحديث | عندما لا يلائم المزوّد محدّثات pi-ai المشتركة |
| 25 | buildAuthDoctorHint |
تلميح إصلاح يُلحق عند فشل تحديث OAuth | عندما يحتاج المزوّد إلى إرشادات إصلاح مصادقة يملكها المزوّد بعد فشل التحديث |
| 26 | matchesContextOverflowError |
مطابق تجاوز نافذة السياق الذي يملكه المزوّد | عندما لدى المزوّد أخطاء تجاوز خام قد تفوتها الاستدلالات العامة |
| 27 | classifyFailoverReason |
تصنيف سبب التحويل الاحتياطي الذي يملكه المزوّد | عندما يستطيع المزوّد تعيين أخطاء API/النقل الخام إلى حدّ المعدل/الحمل الزائد/إلخ |
| 28 | isCacheTtlEligible |
سياسة ذاكرة التخزين المؤقت للمطالبات لمزوّدي الوكيل/النقل الخلفي | عندما يحتاج المزوّد إلى ضبط أهلية TTL لذاكرة التخزين المؤقت خاص بالوكيل |
| 29 | buildMissingAuthMessage |
بديل لرسالة الاسترداد العامة عند غياب المصادقة | عندما يحتاج المزوّد إلى تلميح استرداد خاص بالمزوّد عند غياب المصادقة |
| 30 | augmentModelCatalog |
صفوف كتالوج اصطناعية/نهائية تُلحق بعد الاكتشاف | عندما يحتاج المزوّد إلى صفوف توافق أمامي اصطناعية في models list وأدوات الاختيار |
| 31 | resolveThinkingProfile |
مجموعة مستويات /think الخاصة بالنموذج، وتسميات العرض، والقيمة الافتراضية |
عندما يعرّض المزوّد سلم تفكير مخصصًا أو تسمية ثنائية لنماذج محددة |
| 32 | isBinaryThinking |
hook توافق مفتاح تشغيل/إيقاف الاستدلال | عندما يعرّض المزوّد تفكيرًا ثنائيًا للتشغيل/الإيقاف فقط |
| 33 | supportsXHighThinking |
hook توافق دعم الاستدلال xhigh |
عندما يريد المزوّد xhigh على مجموعة فرعية فقط من النماذج |
| 34 | resolveDefaultThinkingLevel |
hook توافق مستوى /think الافتراضي |
عندما يملك المزوّد سياسة /think افتراضية لعائلة نماذج |
| 35 | isModernModelRef |
مطابق النموذج الحديث لفلاتر الملف الشخصي الحية واختيار smoke | عندما يملك المزوّد مطابقة النموذج المفضل لـ live/smoke |
| 36 | prepareRuntimeAuth |
استبدال اعتماد مُعدّ بالرمز/المفتاح الفعلي في وقت التشغيل قبل الاستدلال مباشرة | عندما يحتاج المزوّد إلى تبادل رمز أو اعتماد طلب قصير العمر |
| 37 | resolveUsageAuth |
حلّ بيانات اعتماد الاستخدام/الفوترة لـ /usage وأسطح الحالة ذات الصلة |
يحتاج المزوّد إلى تحليل مخصّص لرمز الاستخدام/الحصة أو إلى بيانات اعتماد استخدام مختلفة |
| 38 | fetchUsageSnapshot |
جلب لقطات استخدام/حصة خاصة بالمزوّد وتطبيعها بعد حلّ المصادقة | يحتاج المزوّد إلى نقطة نهاية استخدام خاصة بالمزوّد أو محلّل حمولة |
| 39 | createEmbeddingProvider |
بناء محوّل تضمين مملوك للمزوّد للذاكرة/البحث | ينتمي سلوك تضمين الذاكرة إلى Plugin المزوّد |
| 40 | buildReplayPolicy |
إرجاع سياسة إعادة تشغيل تتحكم في معالجة النصوص للمزوّد | يحتاج المزوّد إلى سياسة نصوص مخصّصة (على سبيل المثال، إزالة كتل التفكير) |
| 41 | sanitizeReplayHistory |
إعادة كتابة سجل إعادة التشغيل بعد التنظيف العام للنصوص | يحتاج المزوّد إلى عمليات إعادة كتابة لإعادة التشغيل خاصة بالمزوّد تتجاوز مساعدات Compaction المشتركة |
| 42 | validateReplayTurns |
التحقق النهائي من أدوار إعادة التشغيل أو إعادة تشكيلها قبل المشغّل المضمّن | يحتاج نقل المزوّد إلى تحقق أكثر صرامة من الأدوار بعد التنقية العامة |
| 43 | onModelSelected |
تشغيل آثار جانبية مملوكة للمزوّد بعد الاختيار | يحتاج المزوّد إلى القياس عن بُعد أو حالة مملوكة للمزوّد عندما يصبح نموذج نشطًا |
normalizeModelId وnormalizeTransport وnormalizeConfig تتحقق أولاً من
Plugin المزوّد المطابق، ثم تنتقل إلى Plugins مزوّدين آخرين قادرين على الخطافات
إلى أن يغيّر أحدها فعلياً معرّف النموذج أو النقل/الإعدادات. هذا يُبقي
حشوات alias/compat للمزوّدين عاملة من دون أن يحتاج المستدعي إلى معرفة أي
Plugin مضمّن يملك إعادة الكتابة. إذا لم يُعد أي خطاف مزوّد كتابة إدخال إعدادات
مدعوم من عائلة Google، فسيظل مُطبّع إعدادات Google المضمّن يطبّق تنظيف التوافق ذلك.
إذا كان المزوّد يحتاج إلى بروتوكول سلكي مخصص بالكامل أو منفّذ طلبات مخصص، فهذا صنف مختلف من الامتدادات. هذه الخطافات مخصصة لسلوك المزوّد الذي لا يزال يعمل ضمن حلقة الاستدلال العادية في OpenClaw.
مثال على مزوّد
api.registerProvider({
id: "example-proxy",
label: "Example Proxy",
auth: [],
catalog: {
order: "simple",
run: async (ctx) => {
const apiKey = ctx.resolveProviderApiKey("example-proxy").apiKey;
if (!apiKey) {
return null;
}
return {
provider: {
baseUrl: "https://proxy.example.com/v1",
apiKey,
api: "openai-completions",
models: [{ id: "auto", name: "Auto" }],
},
};
},
},
resolveDynamicModel: (ctx) => ({
id: ctx.modelId,
name: ctx.modelId,
provider: "example-proxy",
api: "openai-completions",
baseUrl: "https://proxy.example.com/v1",
reasoning: false,
input: ["text"],
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
contextWindow: 128000,
maxTokens: 8192,
}),
prepareRuntimeAuth: async (ctx) => {
const exchanged = await exchangeToken(ctx.apiKey);
return {
apiKey: exchanged.token,
baseUrl: exchanged.baseUrl,
expiresAt: exchanged.expiresAt,
};
},
resolveUsageAuth: async (ctx) => {
const auth = await ctx.resolveOAuthToken();
return auth ? { token: auth.token } : null;
},
fetchUsageSnapshot: async (ctx) => {
return await fetchExampleProxyUsage(ctx.token, ctx.timeoutMs, ctx.fetchFn);
},
});
أمثلة مضمّنة
تجمع Plugins المزوّدين المضمّنة الخطافات أعلاه لتناسب احتياجات كل مورّد من
الكتالوج، والمصادقة، والتفكير، وإعادة التشغيل، والاستخدام. تعيش مجموعة الخطافات
الموثوقة مع كل Plugin ضمن extensions/؛ توضّح هذه الصفحة الأشكال بدلاً من
مطابقة القائمة.
مزوّدو الكتالوج بالتمرير المباشر
OpenRouter وKilocode وZ.AI وxAI تسجّل catalog إضافة إلى
resolveDynamicModel / prepareDynamicModel حتى تتمكن من عرض معرّفات
النماذج من المنبع قبل كتالوج OpenClaw الثابت.
مزوّدو OAuth ونقاط نهاية الاستخدام
GitHub Copilot وGemini CLI وChatGPT Codex وMiniMax وXiaomi وz.ai تقرن
prepareRuntimeAuth أو formatApiKey مع resolveUsageAuth +
fetchUsageSnapshot لامتلاك تبادل الرموز ودمج /usage.
عائلات إعادة التشغيل وتنظيف النصوص
العائلات المشتركة المسماة (google-gemini وpassthrough-gemini
وanthropic-by-model وhybrid-anthropic-openai) تتيح للمزوّدين الاشتراك في
سياسة النصوص عبر buildReplayPolicy بدلاً من أن يعيد كل Plugin تنفيذ التنظيف.
مزوّدو الكتالوج فقط
byteplus وcloudflare-ai-gateway وhuggingface وkimi-coding وnvidia
وqianfan وsynthetic وtogether وvenice وvercel-ai-gateway و
volcengine تسجّل catalog فقط وتستخدم حلقة الاستدلال المشتركة.
مساعدات البث الخاصة بـ Anthropic
رؤوس Beta، و/fast / serviceTier، وcontext1m تعيش داخل
واجهة Plugin العامة لـ Anthropic في api.ts / contract-api.ts
(wrapAnthropicProviderStream وresolveAnthropicBetas
وresolveAnthropicFastMode وresolveAnthropicServiceTier) بدلاً من
SDK العام.
مساعدات وقت التشغيل
يمكن لـ Plugins الوصول إلى مساعدات أساسية مختارة عبر api.runtime. بالنسبة إلى TTS:
const clip = await api.runtime.tts.textToSpeech({
text: "Hello from OpenClaw",
cfg: api.config,
});
const result = await api.runtime.tts.textToSpeechTelephony({
text: "Hello from OpenClaw",
cfg: api.config,
});
const voices = await api.runtime.tts.listVoices({
provider: "elevenlabs",
cfg: api.config,
});
ملاحظات:
textToSpeechتُرجع حمولة مخرجات TTS الأساسية العادية لأسطح الملفات/الملاحظات الصوتية.- تستخدم إعدادات
messages.ttsالأساسية واختيار المزوّد. - تُرجع مخزن صوت PCM المؤقت + معدل العينة. يجب على Plugins إعادة أخذ العينات/الترميز للمزوّدين.
listVoicesاختيارية لكل مزوّد. استخدمها لمنتقيات الأصوات أو تدفقات الإعداد التي يملكها المورّد.- يمكن أن تتضمن قوائم الأصوات بيانات وصفية أغنى مثل اللغة، والنوع، ووسوم الشخصية لمنتقيات واعية بالمزوّد.
- OpenAI وElevenLabs يدعمان الهاتفية اليوم. Microsoft لا يدعمها.
يمكن لـ Plugins أيضاً تسجيل مزوّدي الكلام عبر api.registerSpeechProvider(...).
api.registerSpeechProvider({
id: "acme-speech",
label: "Acme Speech",
isConfigured: ({ config }) => Boolean(config.messages?.tts),
synthesize: async (req) => {
return {
audioBuffer: Buffer.from([]),
outputFormat: "mp3",
fileExtension: ".mp3",
voiceCompatible: false,
};
},
});
ملاحظات:
- أبقِ سياسة TTS والرجوع الاحتياطي وتسليم الرد في الأساس.
- استخدم مزوّدي الكلام لسلوك التركيب الذي يملكه المورّد.
- يتم تطبيع إدخال Microsoft القديم
edgeإلى معرّف المزوّدmicrosoft. - نموذج الملكية المفضل موجّه حسب الشركة: يمكن لـ Plugin مورّد واحد أن يملك مزوّدي النص، والكلام، والصورة، والوسائط المستقبلية بينما يضيف OpenClaw عقود القدرات تلك.
لفهم الصور/الصوت/الفيديو، تسجّل Plugins مزوّد فهم وسائط ذا نوع واحد بدلاً من حقيبة مفتاح/قيمة عامة:
api.registerMediaUnderstandingProvider({
id: "google",
capabilities: ["image", "audio", "video"],
describeImage: async (req) => ({ text: "..." }),
transcribeAudio: async (req) => ({ text: "..." }),
describeVideo: async (req) => ({ text: "..." }),
});
ملاحظات:
- أبقِ التنسيق، والرجوع الاحتياطي، والإعدادات، وتوصيل القناة في الأساس.
- أبقِ سلوك المورّد في Plugin المزوّد.
- يجب أن يبقى التوسّع الإضافي ذا نوع محدد: طرائق اختيارية جديدة، وحقول نتائج اختيارية جديدة، وقدرات اختيارية جديدة.
- يتبع توليد الفيديو النمط نفسه بالفعل:
- الأساس يملك عقد القدرة ومساعد وقت التشغيل
- Plugins المورّدين تسجّل
api.registerVideoGenerationProvider(...) - Plugins الميزات/القنوات تستهلك
api.runtime.videoGeneration.*
بالنسبة إلى مساعدات وقت تشغيل فهم الوسائط، يمكن لـ Plugins استدعاء:
const image = await api.runtime.mediaUnderstanding.describeImageFile({
filePath: "/tmp/inbound-photo.jpg",
cfg: api.config,
agentDir: "/tmp/agent",
});
const video = await api.runtime.mediaUnderstanding.describeVideoFile({
filePath: "/tmp/inbound-video.mp4",
cfg: api.config,
});
بالنسبة إلى تفريغ الصوت، يمكن لـ Plugins استخدام وقت تشغيل فهم الوسائط أو الاسم المستعار الأقدم STT:
const { text } = await api.runtime.mediaUnderstanding.transcribeAudioFile({
filePath: "/tmp/inbound-audio.ogg",
cfg: api.config,
// Optional when MIME cannot be inferred reliably:
mime: "audio/ogg",
});
ملاحظات:
api.runtime.mediaUnderstanding.*هو السطح المشترك المفضل لفهم الصور/الصوت/الفيديو.- يستخدم إعدادات صوت فهم الوسائط الأساسية (
tools.media.audio) وترتيب الرجوع الاحتياطي للمزوّدين. - يُرجع
{ text: undefined }عندما لا يتم إنتاج مخرجات تفريغ (مثلاً إدخال تم تخطيه/غير مدعوم). - يبقى
api.runtime.stt.transcribeAudioFile(...)كاسم مستعار للتوافق.
يمكن لـ Plugins أيضاً إطلاق عمليات subagent خلفية عبر api.runtime.subagent:
const result = await api.runtime.subagent.run({
sessionKey: "agent:main:subagent:search-helper",
message: "Expand this query into focused follow-up searches.",
provider: "openai",
model: "gpt-4.1-mini",
deliver: false,
});
ملاحظات:
providerوmodelتجاوزان اختياريان لكل تشغيل، وليسا تغييرات جلسة دائمة.- لا يحترم OpenClaw حقول التجاوز هذه إلا للمتصلين الموثوقين.
- بالنسبة إلى عمليات الرجوع الاحتياطي التي يملكها Plugin، يجب على المشغّلين الاشتراك عبر
plugins.entries.<id>.subagent.allowModelOverride: true. - استخدم
plugins.entries.<id>.subagent.allowedModelsلتقييد Plugins الموثوقة إلى أهدافprovider/modelقانونية محددة، أو"*"للسماح بأي هدف صراحة. - لا تزال عمليات subagent من Plugins غير الموثوقة تعمل، لكن تُرفض طلبات التجاوز بدلاً من الرجوع الاحتياطي بصمت.
- تُوسم جلسات subagent التي تنشئها Plugins بمعرّف Plugin المُنشئ. قد يحذف الرجوع الاحتياطي
api.runtime.subagent.deleteSession(...)تلك الجلسات المملوكة فقط؛ لا يزال حذف الجلسات الاعتباطية يتطلب طلب Gateway بنطاق مسؤول.
للبحث على الويب، يمكن لـ Plugins استهلاك مساعد وقت التشغيل المشترك بدلاً من الوصول إلى توصيل أدوات الوكيل:
const providers = api.runtime.webSearch.listProviders({
config: api.config,
});
const result = await api.runtime.webSearch.search({
config: api.config,
args: {
query: "OpenClaw plugin runtime helpers",
count: 5,
},
});
يمكن لـ Plugins أيضاً تسجيل مزوّدي البحث على الويب عبر
api.registerWebSearchProvider(...).
ملاحظات:
- أبقِ اختيار المزوّد، وحل بيانات الاعتماد، ودلالات الطلب المشتركة في الأساس.
- استخدم مزوّدي البحث على الويب لنقل البحث الخاص بالمورّد.
api.runtime.webSearch.*هو السطح المشترك المفضل لـ Plugins الميزات/القنوات التي تحتاج إلى سلوك البحث من دون الاعتماد على مغلّف أداة الوكيل.
api.runtime.imageGeneration
const result = await api.runtime.imageGeneration.generate({
config: api.config,
args: { prompt: "A friendly lobster mascot", size: "1024x1024" },
});
const providers = api.runtime.imageGeneration.listProviders({
config: api.config,
});
generate(...): توليد صورة باستخدام سلسلة مزوّدي توليد الصور المكوّنة.listProviders(...): سرد مزوّدي توليد الصور المتاحين وقدراتهم.
مسارات HTTP في Gateway
يمكن لـ Plugins كشف نقاط نهاية HTTP باستخدام api.registerHttpRoute(...).
api.registerHttpRoute({
path: "/acme/webhook",
auth: "plugin",
match: "exact",
handler: async (_req, res) => {
res.statusCode = 200;
res.end("ok");
return true;
},
});
حقول المسار:
path: مسار التوجيه ضمن خادم HTTP في Gateway.auth: مطلوب. استخدم"gateway"لطلب مصادقة Gateway العادية، أو"plugin"للمصادقة/التحقق من Webhook المُدار بواسطة Plugin.match: اختياري."exact"(الافتراضي) أو"prefix".replaceExisting: اختياري. يسمح لـ Plugin نفسه باستبدال تسجيل مساره الموجود.handler: أرجعtrueعندما يكون المسار قد عالج الطلب.
ملاحظات:
- تمت إزالة
api.registerHttpHandler(...)وسيتسبب ذلك في خطأ تحميل Plugin. استخدمapi.registerHttpRoute(...)بدلًا منه. - يجب أن تعلن مسارات Plugin عن
authصراحةً. - تُرفض تعارضات
path + matchالدقيقة ما لم يكنreplaceExisting: true، ولا يمكن لأي Plugin استبدال مسار Plugin آخر. - تُرفض المسارات المتداخلة ذات مستويات
authالمختلفة. أبقِ سلاسل التمرير الاحتياطيexact/prefixعلى مستوى المصادقة نفسه فقط. - لا تتلقى مسارات
auth: "plugin"نطاقات وقت تشغيل المشغّل تلقائيًا. فهي مخصصة لـ webhooks المُدارة بواسطة Plugin/التحقق من التوقيع، وليست لاستدعاءات مساعد Gateway ذات الامتيازات. - تعمل مسارات
auth: "gateway"داخل نطاق وقت تشغيل طلب Gateway، لكن هذا النطاق محافظ عمدًا:- مصادقة الحامل بالسر المشترك (
gateway.auth.mode = "token"/"password") تُبقي نطاقات وقت تشغيل مسار Plugin مثبتة علىoperator.write، حتى إذا أرسل المستدعيx-openclaw-scopes - أوضاع HTTP الموثوقة الحاملة للهوية (مثل
trusted-proxyأوgateway.auth.mode = "none"على مدخل خاص) تحترمx-openclaw-scopesفقط عندما يكون الرأس موجودًا صراحةً - إذا غاب
x-openclaw-scopesعن طلبات مسار Plugin الحاملة للهوية هذه، يعود نطاق وقت التشغيل إلىoperator.write
- مصادقة الحامل بالسر المشترك (
- قاعدة عملية: لا تفترض أن مسار Plugin بمصادقة Gateway هو سطح إدارة ضمني. إذا كان مسارك يحتاج إلى سلوك مخصص للمسؤولين فقط، فاشترط وضع مصادقة حاملًا للهوية ووثّق عقد رأس
x-openclaw-scopesالصريح.
مسارات استيراد SDK الخاص بـ Plugin
استخدم المسارات الفرعية الضيقة لـ SDK بدلًا من برميل الجذر الأحادي openclaw/plugin-sdk
عند إنشاء Plugins جديدة. المسارات الفرعية الأساسية:
| المسار الفرعي | الغرض |
|---|---|
openclaw/plugin-sdk/plugin-entry |
أساسيات تسجيل Plugin |
openclaw/plugin-sdk/channel-core |
مساعدات إدخال/بناء القناة |
openclaw/plugin-sdk/core |
مساعدات مشتركة عامة وعقد شامل |
openclaw/plugin-sdk/config-schema |
مخطط Zod الجذري لـ openclaw.json (OpenClawSchema) |
تختار Channel plugins من عائلة من نقاط الاتصال الضيقة — channel-setup,
setup-runtime, setup-adapter-runtime, setup-tools, channel-pairing,
channel-contract, channel-feedback, channel-inbound, channel-lifecycle,
channel-reply-pipeline, command-auth, secret-input, webhook-ingress,
channel-targets, وchannel-actions. يجب توحيد سلوك الموافقة
على عقد approvalCapability واحد بدلًا من الخلط بين
حقول Plugin غير المرتبطة. راجع Channel plugins.
توجد مساعدات وقت التشغيل والإعدادات تحت مسارات فرعية مركزة مطابقة من نوع *-runtime
(approval-runtime, agent-runtime, lazy-runtime, directory-runtime,
text-runtime, runtime-store, system-event-runtime, heartbeat-runtime,
channel-activity-runtime, إلخ). فضّل config-types,
plugin-config-runtime, runtime-config-snapshot، وconfig-mutation
بدلًا من برميل التوافق الواسع config-runtime.
نقاط الإدخال الداخلية للمستودع (لكل جذر حزمة Plugin مضمّن):
index.js— إدخال Plugin المضمّنapi.js— برميل المساعدات/الأنواعruntime-api.js— برميل مخصص لوقت التشغيل فقطsetup-entry.js— إدخال Plugin الإعداد
يجب أن تستورد Plugins الخارجية المسارات الفرعية openclaw/plugin-sdk/* فقط. لا
تستورد src/* الخاص بحزمة Plugin أخرى من النواة أو من Plugin آخر مطلقًا.
تفضّل نقاط الإدخال المحمّلة عبر الواجهة لقطة إعدادات وقت التشغيل النشطة عندما
توجد، ثم تعود إلى ملف الإعدادات المحلول على القرص.
توجد مسارات فرعية مخصصة للقدرات مثل image-generation، وmedia-understanding،
وspeech لأن Plugins المضمّنة تستخدمها اليوم. وهي ليست
عقودًا خارجية مجمّدة تلقائيًا على المدى الطويل — تحقق من صفحة مرجع SDK
ذات الصلة عند الاعتماد عليها.
مخططات أداة الرسائل
يجب أن تمتلك Plugins مساهمات مخطط describeMessageTool(...) الخاصة بالقناة
للأساسيات غير الرسائل مثل التفاعلات والقراءات والاستطلاعات.
يجب أن يستخدم عرض الإرسال المشترك عقد MessagePresentation العام
بدلًا من حقول الأزرار أو المكونات أو الكتل أو البطاقات الأصلية لدى المزوّد.
راجع Message Presentation لمعرفة العقد،
وقواعد الرجوع الاحتياطي، وتخطيط المزوّد، وقائمة تحقق مؤلف Plugin.
تعلن Plugins القادرة على الإرسال عما يمكنها عرضه من خلال قدرات الرسائل:
presentationلكتل العرض الدلالية (text,context,divider,buttons,select)delivery-pinلطلبات التسليم المثبّت
تقرر النواة ما إذا كانت ستعرض العرض أصليًا أم تخفّضه إلى نص. لا تكشف منافذ هروب واجهة مستخدم أصلية للمزوّد من أداة الرسائل العامة. تبقى مساعدات SDK المهملة للمخططات الأصلية القديمة مصدّرة من أجل Plugins خارجية موجودة، لكن لا ينبغي أن تستخدمها Plugins الجديدة.
حل أهداف القناة
يجب أن تمتلك Channel plugins دلالات الأهداف الخاصة بالقناة. أبقِ المضيف الصادر المشترك عامًا واستخدم سطح محوّل الرسائل لقواعد المزوّد:
messaging.inferTargetChatType({ to })يقرر ما إذا كان الهدف المطبّع يجب أن يُعامل كـdirectأوgroupأوchannelقبل البحث في الدليل.messaging.targetResolver.looksLikeId(raw, normalized)يخبر النواة ما إذا كان الإدخال يجب أن يتجاوز مباشرةً إلى حل شبيه بالمعرّف بدلًا من البحث في الدليل.messaging.targetResolver.resolveTarget(...)هو الرجوع الاحتياطي الخاص بـ Plugin عندما تحتاج النواة إلى حل نهائي يملكه المزوّد بعد التطبيع أو بعد فشل البحث في الدليل.messaging.resolveOutboundSessionRoute(...)يمتلك إنشاء مسار الجلسة الخاص بالمزوّد بمجرد حل الهدف.
التقسيم الموصى به:
- استخدم
inferTargetChatTypeلقرارات الفئة التي يجب أن تحدث قبل البحث في الأقران/المجموعات. - استخدم
looksLikeIdلفحوصات "عامل هذا كمعرّف هدف صريح/أصلي". - استخدم
resolveTargetللرجوع الاحتياطي للتطبيع الخاص بالمزوّد، وليس للبحث الواسع في الدليل. - أبقِ المعرّفات الأصلية للمزوّد مثل معرّفات الدردشة، ومعرّفات السلاسل، وJIDs، والمقابض، ومعرّفات الغرف
داخل قيم
targetأو المعاملات الخاصة بالمزوّد، وليس في حقول SDK العامة.
الأدلة المدعومة بالإعدادات
يجب أن تبقي Plugins التي تشتق إدخالات الدليل من الإعدادات ذلك المنطق داخل
Plugin وأن تعيد استخدام المساعدات المشتركة من
openclaw/plugin-sdk/directory-runtime.
استخدم هذا عندما تحتاج قناة إلى أقران/مجموعات مدعومة بالإعدادات مثل:
- أقران DM مدفوعين بقائمة سماح
- خرائط قنوات/مجموعات مهيأة
- بدائل دليل ثابتة ضمن نطاق الحساب
تتعامل المساعدات المشتركة في directory-runtime مع العمليات العامة فقط:
- ترشيح الاستعلامات
- تطبيق الحدود
- مساعدات إزالة التكرار/التطبيع
- بناء
ChannelDirectoryEntry[]
يجب أن يبقى فحص الحساب الخاص بالقناة وتطبيع المعرّفات في تنفيذ Plugin.
كتالوجات المزوّدين
يمكن لـ Provider plugins تعريف كتالوجات نماذج للاستدلال باستخدام
registerProvider({ catalog: { run(...) { ... } } }).
يعيد catalog.run(...) الشكل نفسه الذي يكتبه OpenClaw في
models.providers:
{ provider }لإدخال مزوّد واحد{ providers }لعدة إدخالات مزوّدين
استخدم catalog عندما يملك Plugin معرّفات نماذج خاصة بالمزوّد، أو افتراضيات URL الأساسي،
أو بيانات تعريف نماذج مقيدة بالمصادقة.
يتحكم catalog.order في توقيت دمج كتالوج Plugin بالنسبة إلى المزوّدين
الضمنيين المدمجين في OpenClaw:
simple: مزوّدون بسيطون مدفوعون بمفتاح API أو envprofile: مزوّدون يظهرون عند وجود ملفات تعريف مصادقةpaired: مزوّدون ينشئون عدة إدخالات مزوّدين ذات صلةlate: التمريرة الأخيرة، بعد المزوّدين الضمنيين الآخرين
تفوز المزوّدات اللاحقة عند تصادم المفاتيح، لذا يمكن لـ Plugins تجاوز إدخال مزوّد مدمج عمدًا بالمعرّف نفسه.
التوافق:
- لا يزال
discoveryيعمل كاسم مستعار قديم - إذا سُجل كل من
catalogوdiscovery، يستخدم OpenClawcatalog
فحص القنوات للقراءة فقط
إذا سجّل Plugin لديك قناة، ففضّل تنفيذ
plugin.config.inspectAccount(cfg, accountId) إلى جانب resolveAccount(...).
السبب:
resolveAccount(...)هو مسار وقت التشغيل. يُسمح له بافتراض أن بيانات الاعتماد قد جُسدت بالكامل وأن يفشل سريعًا عند غياب الأسرار المطلوبة.- يجب ألا تحتاج مسارات أوامر القراءة فقط مثل
openclaw status، وopenclaw status --all، وopenclaw channels status، وopenclaw channels resolve، وتدفقات إصلاح doctor/config إلى تجسيد بيانات اعتماد وقت التشغيل لمجرد وصف الإعدادات.
سلوك inspectAccount(...) الموصى به:
- أعد حالة حساب وصفية فقط.
- حافظ على
enabledوconfigured. - ضمّن حقول مصدر/حالة بيانات الاعتماد عند اللزوم، مثل:
tokenSource,tokenStatusbotTokenSource,botTokenStatusappTokenSource,appTokenStatussigningSecretSource,signingSecretStatus
- لا تحتاج إلى إرجاع قيم الرمز الخام لمجرد الإبلاغ عن
توفر القراءة فقط. يكفي إرجاع
tokenStatus: "available"(وحقل المصدر المطابق) لأوامر نمط الحالة. - استخدم
configured_unavailableعندما تكون بيانات الاعتماد مهيأة عبر SecretRef لكنها غير متاحة في مسار الأمر الحالي.
يتيح ذلك لأوامر القراءة فقط الإبلاغ عن "مهيأ لكن غير متاح في مسار الأمر هذا" بدلًا من التعطل أو الإبلاغ خطأً عن أن الحساب غير مهيأ.
حزم الحزم
قد يتضمن دليل Plugin ملف package.json يحتوي على openclaw.extensions:
{
"name": "my-pack",
"openclaw": {
"extensions": ["./src/safety.ts", "./src/tools.ts"],
"setupEntry": "./src/setup-entry.ts"
}
}
يصبح كل إدخال Plugin. إذا أدرجت الحزمة عدة extensions، يصبح معرّف Plugin
هو name/<fileBase>.
إذا كان Plugin لديك يستورد تبعيات npm، فثبّتها في ذلك الدليل حتى يكون
node_modules متاحًا (npm install / pnpm install).
حاجز أمان: يجب أن يبقى كل إدخال openclaw.extensions داخل دليل Plugin
بعد حل الروابط الرمزية. تُرفض الإدخالات التي تهرب من دليل الحزمة.
ملاحظة أمان: يقوم openclaw plugins install بتثبيت تبعيات Plugin باستخدام
npm install --omit=dev --ignore-scripts محليًا للمشروع (بلا سكربتات دورة حياة،
ولا تبعيات تطوير في وقت التشغيل)، مع تجاهل إعدادات تثبيت npm العامة الموروثة.
أبقِ أشجار تبعيات Plugin "JS/TS خالصة" وتجنب الحزم التي تتطلب
بناءات postinstall.
اختياري: يمكن أن يشير openclaw.setupEntry إلى وحدة إعداد خفيفة فقط.
عندما يحتاج OpenClaw إلى أسطح إعداد لقناة Plugin معطلة، أو
عندما تكون قناة Plugin مفعلة لكنها لا تزال غير مهيأة، يحمّل setupEntry
بدلًا من إدخال Plugin الكامل. هذا يجعل بدء التشغيل والإعداد أخف
عندما يربط إدخال Plugin الرئيسي أيضًا الأدوات أو الخطافات أو شيفرة أخرى مخصصة لوقت التشغيل فقط.
اختياري: يمكن لـ openclaw.startup.deferConfiguredChannelFullLoadUntilAfterListen
أن يجعل قناة Plugin تستخدم مسار setupEntry نفسه أثناء مرحلة بدء تشغيل Gateway
قبل الاستماع، حتى عندما تكون القناة مهيأة بالفعل.
استخدم هذا فقط عندما يغطي setupEntry بالكامل سطح بدء التشغيل الذي يجب أن يوجد
قبل أن يبدأ Gateway بالاستماع. عمليًا، يعني ذلك أن إدخال الإعداد
يجب أن يسجل كل قدرة مملوكة للقناة يعتمد عليها بدء التشغيل، مثل:
- تسجيل القناة نفسه
- أي مسارات HTTP يجب أن تكون متاحة قبل أن يبدأ Gateway بالاستماع
- أي طرق أو أدوات أو خدمات Gateway يجب أن توجد خلال النافذة نفسها
إذا كان الإدخال الكامل لديك لا يزال يملك أي قدرة بدء تشغيل مطلوبة، فلا تفعّل هذا العلم. أبقِ Plugin على السلوك الافتراضي ودع OpenClaw يحمّل الإدخال الكامل أثناء بدء التشغيل.
يمكن للقنوات المضمّنة أيضًا نشر مساعدات سطح عقد مخصصة للإعداد فقط يمكن للنواة استشارتها قبل تحميل وقت تشغيل القناة الكامل. سطح ترقية الإعداد الحالي هو:
singleAccountKeysToMovenamedAccountPromotionKeysresolveSingleAccountPromotionTarget(...)
يستخدم core هذا السطح عندما يحتاج إلى ترقية إعداد قناة حساب واحد قديم
إلى channels.<id>.accounts.* من دون تحميل مدخل Plugin الكامل.
Matrix هو المثال المضمّن الحالي: فهو ينقل مفاتيح auth/bootstrap فقط إلى حساب
مُرقّى مُسمّى عندما تكون الحسابات المُسمّاة موجودة بالفعل، ويمكنه الحفاظ على
مفتاح حساب افتراضي غير معياري مُهيأ بدلًا من إنشاء
accounts.default دائمًا.
تحافظ محوّلات رقع الإعداد تلك على اكتشاف سطح العقد المضمّن كسولًا. يبقى وقت الاستيراد خفيفًا؛ ولا يُحمّل سطح الترقية إلا عند أول استخدام بدلًا من إعادة الدخول إلى بدء تشغيل القناة المضمّنة عند استيراد الوحدة.
عندما تتضمن أسطح بدء التشغيل تلك طرائق Gateway RPC، أبقها على بادئة خاصة
بالـ Plugin. تبقى مساحات أسماء إدارة core (config.*,
exec.approvals.*, wizard.*, update.*) محجوزة وتُحل دائمًا
إلى operator.admin، حتى إذا طلب Plugin نطاقًا أضيق.
مثال:
{
"name": "@scope/my-channel",
"openclaw": {
"extensions": ["./index.ts"],
"setupEntry": "./setup-entry.ts",
"startup": {
"deferConfiguredChannelFullLoadUntilAfterListen": true
}
}
}
بيانات كتالوج القنوات الوصفية
يمكن لـ plugins القنوات الإعلان عن بيانات إعداد/اكتشاف وصفية عبر openclaw.channel و
تلميحات التثبيت عبر openclaw.install. هذا يُبقي كتالوج core بلا بيانات.
مثال:
{
"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": "<bundled-plugin-local-path>",
"defaultChoice": "npm"
}
}
}
حقول openclaw.channel المفيدة خارج المثال الأدنى:
detailLabel: تسمية ثانوية لأسطح كتالوج/حالة أكثر ثراءًdocsLabel: تجاوز نص الرابط لرابط الوثائقpreferOver: معرّفات Plugin/قناة ذات أولوية أدنى ينبغي أن يتقدم عليها إدخال الكتالوج هذاselectionDocsPrefix,selectionDocsOmitLabel,selectionExtras: عناصر تحكم بنسخة سطح الاختيارmarkdownCapable: يوسم القناة بأنها قادرة على markdown لقرارات تنسيق الصادرexposure.configured: إخفاء القناة من أسطح سرد القنوات المهيأة عند ضبطها علىfalseexposure.setup: إخفاء القناة من منتقيات الإعداد/التهيئة التفاعلية عند ضبطها علىfalseexposure.docs: وسم القناة بأنها داخلية/خاصة لأسطح تنقل الوثائقshowConfigured/showInSetup: أسماء مستعارة قديمة ما زالت مقبولة للتوافق؛ فضّلexposurequickstartAllowFrom: إدخال القناة في مسار quickstart القياسيallowFromforceAccountBinding: اشتراط ربط حساب صريح حتى عند وجود حساب واحد فقطpreferSessionLookupForAnnounceTarget: تفضيل البحث في الجلسة عند حل أهداف الإعلان
يمكن لـ 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": {...} } } ] }. يقبل المحلل أيضًا "packages" أو "plugins" كأسماء مستعارة قديمة للمفتاح "entries".
تكشف إدخالات كتالوج القنوات المُولّدة وإدخالات كتالوج تثبيت المزوّد
حقائق مصدر تثبيت مطبّعة بجوار كتلة openclaw.install الخام. تحدد
الحقائق المطبّعة ما إذا كان توصيف npm إصدارًا محددًا بدقة أو محددًا عائمًا،
وما إذا كانت بيانات سلامة متوقعة موجودة، وما إذا كان مسار مصدر محلي
متاحًا أيضًا. عندما تكون هوية الكتالوج/الحزمة معروفة، تحذر الحقائق
المطبّعة إذا انحرف اسم حزمة npm المحلل عن تلك الهوية.
وتحذر أيضًا عندما يكون defaultChoice غير صالح أو يشير إلى مصدر
غير متاح، وعندما تكون بيانات سلامة npm موجودة من دون مصدر npm صالح.
ينبغي للمستهلكين التعامل مع installSource كحقل اختياري إضافي بحيث
لا تضطر الإدخالات المصنوعة يدويًا وملاءمات الكتالوج إلى توليده.
هذا يتيح للإعداد الأولي والتشخيصات شرح حالة مستوى المصدر من دون
استيراد وقت تشغيل Plugin.
ينبغي لإدخالات npm الخارجية الرسمية تفضيل npmSpec محدد بدقة مع
expectedIntegrity. لا تزال أسماء الحزم المجردة ووسوم التوزيع تعمل
للتوافق، لكنها تعرض تحذيرات مستوى المصدر حتى يتمكن الكتالوج من التحرك
نحو تثبيتات مثبتة ومتحققة السلامة من دون كسر plugins الحالية.
عندما يثبت الإعداد الأولي من مسار كتالوج محلي، فإنه يسجل إدخال فهرس Plugin
مُدارًا مع source: "path" وsourcePath نسبيًا إلى مساحة العمل عندما يكون ذلك ممكنًا. يبقى مسار التحميل التشغيلي المطلق في
plugins.load.paths؛ ويتجنب سجل التثبيت تكرار مسارات محطة العمل المحلية
في إعداد طويل العمر. هذا يُبقي تثبيتات التطوير المحلي مرئية لتشخيصات
مستوى المصدر من دون إضافة سطح إفصاح ثانٍ لمسار نظام ملفات خام.
فهرس Plugin المحفوظ plugins/installs.json هو مصدر حقيقة التثبيت
ويمكن تحديثه من دون تحميل وحدات وقت تشغيل Plugin.
تبقى خريطة installRecords الخاصة به دائمة حتى عندما يكون بيان Plugin مفقودًا أو
غير صالح؛ ومصفوفة plugins الخاصة به هي عرض بيان قابل لإعادة البناء.
plugins محرك السياق
تمتلك plugins محرك السياق تنسيق سياق الجلسة للإدخال والتجميع
وCompaction. سجّلها من Plugin الخاص بك باستخدام
api.registerContextEngine(id, factory)، ثم اختر المحرك النشط باستخدام
plugins.slots.contextEngine.
استخدم هذا عندما يحتاج Plugin الخاص بك إلى استبدال مسار السياق الافتراضي أو توسيعه بدلًا من مجرد إضافة بحث ذاكرة أو hooks.
export default function (api) {
api.registerContextEngine("lossless-claw", (ctx) => ({
info: { id: "lossless-claw", name: "Lossless Claw", ownsCompaction: true },
async ingest() {
return { ingested: true };
},
async assemble({ messages, availableTools, citationsMode }) {
return {
messages,
estimatedTokens: 0,
systemPromptAddition: buildMemorySystemPromptAddition({
availableTools: availableTools ?? new Set(),
citationsMode,
}),
};
},
async compact() {
return { ok: true, compacted: false };
},
}));
}
يعرض ctx الخاص بالمصنع قيم config وagentDir وworkspaceDir
اختيارية للتهيئة وقت الإنشاء.
إذا كان محركك لا يمتلك خوارزمية Compaction، فأبقِ compact()
منفذة وفوّضها صراحةً:
buildMemorySystemPromptAddition,
delegateCompactionToRuntime,
} from "openclaw/plugin-sdk/core";
export default function (api) {
api.registerContextEngine("my-memory-engine", (ctx) => ({
info: {
id: "my-memory-engine",
name: "My Memory Engine",
ownsCompaction: false,
},
async ingest() {
return { ingested: true };
},
async assemble({ messages, availableTools, citationsMode }) {
return {
messages,
estimatedTokens: 0,
systemPromptAddition: buildMemorySystemPromptAddition({
availableTools: availableTools ?? new Set(),
citationsMode,
}),
};
},
async compact(params) {
return await delegateCompactionToRuntime(params);
},
}));
}
إضافة إمكانية جديدة
عندما يحتاج Plugin إلى سلوك لا يتوافق مع API الحالي، لا تتجاوز نظام Plugin بالوصول الخاص إلى الداخل. أضف الإمكانية الناقصة.
التسلسل الموصى به:
- عرّف عقد core قرر ما السلوك المشترك الذي ينبغي أن يمتلكه core: السياسة، fallback، دمج الإعدادات، دورة الحياة، الدلالات المواجهة للقناة، وشكل مساعد وقت التشغيل.
- أضف أسطح تسجيل/وقت تشغيل Plugin نمطية
وسّع
OpenClawPluginApiو/أوapi.runtimeبأصغر سطح إمكانية نمطي مفيد. - صِل core + مستهلكي القناة/الميزة ينبغي للقنوات وplugins الميزات استهلاك الإمكانية الجديدة عبر core، وليس باستيراد تنفيذ مورّد مباشرةً.
- سجّل تنفيذات المورّدين ثم تسجل plugins المورّدين خلفياتها مقابل الإمكانية.
- أضف تغطية عقد أضف اختبارات حتى تبقى الملكية وشكل التسجيل صريحين بمرور الوقت.
هكذا يبقى OpenClaw ذا رأي واضح من دون أن يصبح مبرمجًا صلبًا وفق رؤية مزود واحد للعالم. راجع كتاب وصفات الإمكانيات للحصول على قائمة تحقق ملفات ملموسة ومثال عملي.
قائمة تحقق الإمكانية
عندما تضيف إمكانية جديدة، ينبغي للتنفيذ عادةً أن يمس هذه الأسطح معًا:
- أنواع عقد core في
src/<capability>/types.ts - مساعد مشغّل/وقت تشغيل core في
src/<capability>/runtime.ts - سطح تسجيل Plugin API في
src/plugins/types.ts - توصيل سجل Plugin في
src/plugins/registry.ts - تعريض وقت تشغيل Plugin في
src/plugins/runtime/*عندما تحتاج plugins الميزة/القناة إلى استهلاكه - مساعدو الالتقاط/الاختبار في
src/test-utils/plugin-registration.ts - تأكيدات الملكية/العقد في
src/plugins/contracts/registry.ts - وثائق المشغّل/Plugin في
docs/
إذا كان أحد تلك الأسطح مفقودًا، فذلك عادةً علامة على أن الإمكانية لم تُدمج بالكامل بعد.
قالب الإمكانية
النمط الأدنى:
// core contract
export type VideoGenerationProviderPlugin = {
id: string;
label: string;
generateVideo: (req: VideoGenerationRequest) => Promise<VideoGenerationResult>;
};
// plugin API
api.registerVideoGenerationProvider({
id: "openai",
label: "OpenAI",
async generateVideo(req) {
return await generateOpenAiVideo(req);
},
});
// shared runtime helper for feature/channel plugins
const clip = await api.runtime.videoGeneration.generate({
prompt: "Show the robot walking through the lab.",
cfg,
});
نمط اختبار العقد:
expect(findVideoGenerationProviderIdsForPlugin("openai")).toEqual(["openai"]);
هذا يُبقي القاعدة بسيطة:
- يمتلك core عقد الإمكانية + التنسيق
- تمتلك plugins المورّدين تنفيذات المورّدين
- تستهلك plugins الميزات/القنوات مساعدات وقت التشغيل
- تُبقي اختبارات العقد الملكية صريحة
ذو صلة
- معمارية Plugin — نموذج الإمكانية العام وأشكالها
- المسارات الفرعية لـ Plugin SDK
- إعداد Plugin SDK
- بناء plugins