تخطَّ إلى المحتوى

تحميل الامتدادات (وحدات TypeScript/JavaScript)

تتناول هذه الوثيقة كيفية اكتشاف وكيل البرمجة وتحميل وحدات الامتدادات (.ts/.js) عند بدء التشغيل.

لا تتناول امتدادات بيان gemini-extension.json (موثقة بشكل منفصل).

ما الذي يقوم به هذا النظام الفرعي

Section titled “ما الذي يقوم به هذا النظام الفرعي”

يقوم تحميل الامتدادات ببناء قائمة بملفات إدخال الوحدات، واستيراد كل وحدة باستخدام Bun، وتنفيذ المصنع الخاص بها، ويُعيد:

  • تعريفات الامتدادات المحملة
  • أخطاء التحميل لكل مسار (دون إيقاف عملية التحميل بأكملها)
  • كائن وقت تشغيل الامتداد المشترك الذي يستخدمه لاحقًا ExtensionRunner

ملفات التنفيذ الأساسية

Section titled “ملفات التنفيذ الأساسية”
  • src/extensibility/extensions/loader.ts — اكتشاف المسار + الاستيراد/التنفيذ
  • src/extensibility/extensions/index.ts — الصادرات العامة
  • src/extensibility/extensions/runner.ts — تنفيذ وقت التشغيل/الأحداث بعد التحميل
  • src/discovery/builtin.ts — موفر الاكتشاف التلقائي الأصلي لوحدات الامتدادات
  • src/config/settings.ts — تحميل إعدادات extensions / disabledExtensions المدمجة

مدخلات تحميل الامتدادات

Section titled “مدخلات تحميل الامتدادات”

1) وحدات الامتدادات الأصلية المكتشفة تلقائيًا

Section titled “1) وحدات الامتدادات الأصلية المكتشفة تلقائيًا”

تبدأ discoverAndLoadExtensions() بطلب العناصر ذات قدرة extension-module من موفري الاكتشاف، ثم تحتفظ بعناصر موفر native فقط.

المواقع الأصلية الفعلية:

  • المشروع: <cwd>/.xcsh/extensions
  • المستخدم: ~/.xcsh/agent/extensions

تأتي جذور المسارات من الموفر الأصلي (SOURCE_PATHS.native).

ملاحظات:

  • يعتمد الاكتشاف التلقائي الأصلي حاليًا على .xcsh.
  • لا يزال يُقبل .pi القديم في مفاتيح بيان package.json (pi.extensions)، لكنه لا يُستخدم كجذر أصلي هنا.

2) المسارات المهيأة صراحةً

Section titled “2) المسارات المهيأة صراحةً”

بعد الاكتشاف التلقائي، تُضاف المسارات المهيأة ويتم حلها.

مصادر المسارات المهيأة في مسار بدء تشغيل الجلسة الرئيسي (sdk.ts):

  1. المسارات المقدمة عبر CLI (--extension/-e، كما يُعامَل --hook أيضًا كمسار امتداد)
  2. مصفوفة extensions في الإعدادات (الإعدادات العامة والمشروع المدمجة)

ملف الإعدادات العامة:

  • ~/.xcsh/agent/config.yml (أو دليل وكيل مخصص عبر PI_CODING_AGENT_DIR)

ملف إعدادات المشروع:

  • <cwd>/.xcsh/settings.json

أمثلة:

~/.xcsh/agent/config.yml
extensions:
- ~/my-exts/safety.ts
- ./local/ext-pack
{
"extensions": ["./.xcsh/extensions/my-extra"]
}

عناصر التحكم في التفعيل/التعطيل

Section titled “عناصر التحكم في التفعيل/التعطيل”
  • CLI: --no-extensions
  • خيار SDK: disableExtensionDiscovery

تقسيم السلوك:

  • SDK: عند تعيين disableExtensionDiscovery=true، لا يزال يحمّل additionalExtensionPaths عبر loadExtensions().
  • بناء مسار CLI (main.ts) يمسح مسارات امتداد CLI حاليًا عند تعيين --no-extensions، لذا لا يتم إعادة توجيه -e/--hook الصريح في هذا الوضع.

تعطيل وحدات امتداد محددة

Section titled “تعطيل وحدات امتداد محددة”

يُصفي إعداد disabledExtensions بناءً على تنسيق معرّف الامتداد:

  • extension-module:<derivedName>

يعتمد derivedName على مسار الإدخال (getExtensionNameFromPath)، على سبيل المثال:

  • /x/foo.ts -> foo
  • /x/bar/index.ts -> bar

مثال:

disabledExtensions:
- extension-module:foo

بالنسبة للمسارات المهيأة:

  1. تطبيع المسافات Unicode
  2. توسيع ~
  3. إذا كان نسبيًا، يُحل بالنسبة إلى cwd الحالي

إذا كان المسار المهيأ ملفًا

Section titled “إذا كان المسار المهيأ ملفًا”

يُستخدم مباشرةً كمرشح لإدخال الوحدة.

إذا كان المسار المهيأ دليلًا

Section titled “إذا كان المسار المهيأ دليلًا”

ترتيب الدقة:

  1. package.json في ذلك الدليل مع xcsh.extensions (أو pi.extensions القديم) -> استخدام الإدخالات المُعلنة
  2. index.ts
  3. index.js
  4. وإلا فحص مستوى واحد بحثًا عن إدخالات الامتداد:
    • *.ts / *.js المباشرة
    • index.ts / index.js في دليل فرعي
    • package.json في دليل فرعي مع xcsh.extensions / pi.extensions

القواعد والقيود:

  • لا يوجد اكتشاف متكرر يتجاوز مستوى دليل فرعي واحد
  • تُحل إدخالات بيان extensions المُعلنة بالنسبة لدليل الحزمة ذلك
  • تُدرج الإدخالات المُعلنة فقط إذا كان الملف موجودًا/الوصول مسموحًا به
  • في أزواج */index.{ts,js}، يُفضَّل TypeScript على JavaScript
  • تُعامَل الروابط الرمزية كملفات/أدلة مؤهلة

يختلف سلوك التجاهل حسب المصدر

Section titled “يختلف سلوك التجاهل حسب المصدر”
  • يستخدم الاكتشاف التلقائي الأصلي (discoverExtensionModulePaths في مساعدات الاكتشاف) glob أصلي مع gitignore: true وhidden: false.
  • لا يطبّق فحص الدليل المهيأ الصريح في loader.ts قواعد readdir ولا يُطبق تصفية gitignore.

ترتيب التحميل والأسبقية

Section titled “ترتيب التحميل والأسبقية”

تبني discoverAndLoadExtensions() قائمة واحدة مرتبة ثم تستدعي loadExtensions().

الترتيب:

  1. الوحدات المكتشفة تلقائيًا الأصلية
  2. المسارات المهيأة الصريحة (بالترتيب المقدم)

في sdk.ts، الترتيب المهيأ هو:

  1. المسارات الإضافية لـ CLI
  2. إعدادات extensions

إلغاء التكرار:

  • يعتمد على المسار المطلق
  • أول مسار مرئي يفوز
  • تُتجاهل التكرارات اللاحقة

الاستنتاج: إذا كان نفس مسار الوحدة مكتشفًا تلقائيًا ومهيأًا صراحةً، فيتم تحميله مرة واحدة في الموضع الأول (مرحلة الاكتشاف التلقائي).


استيراد الوحدة وعقد المصنع

Section titled “استيراد الوحدة وعقد المصنع”

يتم تحميل كل مسار مرشح باستخدام الاستيراد الديناميكي:

  • await import(resolvedPath)
  • المصنع هو module.default ?? module
  • يجب أن يكون المصنع دالة (ExtensionFactory)

إذا لم يكن التصدير دالة، يفشل ذلك المسار بخطأ منظم وتستمر عملية التحميل.


يتم التقاط حالات الفشل لكل مسار امتداد كـ { path, error } ولا تمنع تحميل المسارات الأخرى.

الحالات الشائعة:

  • فشل الاستيراد / ملف مفقود
  • تصدير مصنع غير صالح (غير دالة)
  • استثناء مُلقى أثناء تنفيذ المصنع
  • الامتدادات غير معزولة في صندوق رمل (نفس العملية/وقت التشغيل).
  • تتشارك في EventBus واحد وكائن ExtensionRuntime واحد.
  • أثناء التحميل، تُلقي أساليب إجراء وقت التشغيل عمدًا ExtensionRuntimeNotInitializedError؛ يحدث ربط الإجراءات لاحقًا في ExtensionRunner.initialize().

عند تشغيل الأحداث عبر ExtensionRunner، يتم التقاط استثناءات المعالج وإصدارها كأخطاء امتداد بدلًا من إسقاط حلقة التشغيل.


أمثلة تخطيط المستخدم/المشروع الدنيا

Section titled “أمثلة تخطيط المستخدم/المشروع الدنيا”
~/.xcsh/agent/
config.yml
extensions/
guardrails.ts
audit/
index.ts
<repo>/
.xcsh/
settings.json
extensions/
checks/
package.json
lint-gates.ts

checks/package.json:

{
"xcsh": {
"extensions": ["./src/check-a.ts", "./src/check-b.js"]
}
}

لا يزال يُقبل مفتاح البيان القديم:

{
"pi": {
"extensions": ["./index.ts"]
}
}