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

مدير الإضافات وأنابيب التثبيت

يصف هذا المستند كيفية تأثير عمليات xcsh plugin على حالة الإضافات على القرص، وكيف تُصبح الإضافات المثبّتة قدرات وقت تشغيل (الأدوات حالياً، مع توفر حل مسارات الخطافات/الأوامر).

يوجد تطبيقان لإدارة الإضافات في قاعدة الكود:

  1. المسار النشط المستخدم بواسطة أوامر CLI: PluginManager (src/extensibility/plugins/manager.ts)
  2. وحدة المساعد القديمة: دوال المثبّت (src/extensibility/plugins/installer.ts)

يمر تنفيذ الأمر xcsh plugin ... عبر PluginManager.

لا يزال installer.ts يوثّق فحوصات الأمان المهمة وسلوك نظام الملفات، لكنه ليس المسار المستخدم بواسطة src/commands/plugin.ts + src/cli/plugin-cli.ts.

دورة الحياة: من استدعاء CLI إلى التوفر في وقت التشغيل

Section titled “دورة الحياة: من استدعاء CLI إلى التوفر في وقت التشغيل”
xcsh plugin <action> ...
-> src/commands/plugin.ts
-> runPluginCommand(...) in src/cli/plugin-cli.ts
-> PluginManager method (install/list/uninstall/link/...)
-> mutate ~/.xcsh/plugins/{package.json,node_modules,xcsh-plugins.lock.json}
-> runtime discovery: discoverAndLoadCustomTools(...)
-> getAllPluginToolPaths(cwd)
-> custom tool loader imports tool modules
  • يُعرّف src/commands/plugin.ts الأمر والأعلام ويُحيل إلى runPluginCommand.
  • يُعيّن src/cli/plugin-cli.ts الأوامر الفرعية إلى أساليب PluginManager:
    • install، uninstall، list، link، doctor، features، config، enable، disable
  • لا يوجد إجراء update صريح؛ يتم التحديث عبر إعادة تشغيل install بمواصفة حزمة/إصدار جديدة.

تقع حالة الإضافات العامة تحت ~/.xcsh/plugins:

  • package.json — بيان التبعيات المستخدم بواسطة bun install/bun uninstall
  • node_modules/ — حزم الإضافات المثبّتة أو الروابط الرمزية
  • xcsh-plugins.lock.json — حالة وقت التشغيل:
    • تفعيل/تعطيل لكل إضافة
    • مجموعة الميزات المحددة لكل إضافة
    • إعدادات الإضافة المُحفوظة

تقع التجاوزات المحلية للمشروع في:

  • <cwd>/.xcsh/plugin-overrides.json

التجاوزات للقراءة فقط من منظور المدير/المُحمّل (لا يوجد مسار كتابة هنا)، ويمكنها تعطيل الإضافات أو تجاوز الميزات/الإعدادات لهذا المشروع.

تحليل مواصفة الإضافة وتفسير البيانات الوصفية

Section titled “تحليل مواصفة الإضافة وتفسير البيانات الوصفية”

قواعد نحو مواصفة التثبيت

Section titled “قواعد نحو مواصفة التثبيت”

يدعم parsePluginSpec (parser.ts):

  • pkg -> features: null (السلوك الافتراضي)
  • pkg[*] -> تفعيل جميع ميزات البيان
  • pkg[] -> عدم تفعيل أي ميزات اختيارية
  • pkg[a,b] -> تفعيل الميزات المسمّاة
  • @scope/pkg@1.2.3[feat] -> حزمة محددة النطاق والإصدار مع اختيار ميزات صريح

يُزيل extractPackageName لاحقة الإصدار للبحث عن المسار على القرص بعد التثبيت.

مصدر البيان والحقول المطلوبة

Section titled “مصدر البيان والحقول المطلوبة”

يُحلَّل البيان على النحو التالي:

  1. package.json.xcsh
  2. احتياطي package.json.pi
  3. احتياطي { version: package.version }

الانعكاسات:

  • لا يوجد تحقق صارم من المخطط في المدير/المُحمّل.
  • الحزمة التي تفتقر إلى xcsh/pi لا تزال قابلة للتثبيت والإدراج.
  • يتخطى تحميل الإضافة في وقت التشغيل (getEnabledPlugins) الحزم التي تفتقر إلى بيان xcsh/pi.
  • يُستبدل manifest.version دائماً من version الحزمة.

فشل تحليل JSON في package.json يُعدّ فشلاً صعباً عند القراءة؛ قد يفشل شكل البيان المشوّه لاحقاً فقط عند استهلاك حقول محددة.

تدفق التثبيت/التحديث (PluginManager.install)

Section titled “تدفق التثبيت/التحديث (PluginManager.install)”
  1. تحليل صيغة أقواس الميزات من مواصفة التثبيت.
  2. التحقق من اسم الحزمة بواسطة regex + قائمة رفض أحرف shell الخاصة.
  3. التأكد من وجود package.json للإضافة (xcsh-plugins، خريطة التبعيات الخاصة).
  4. تشغيل bun install <packageSpec> في ~/.xcsh/plugins.
  5. قراءة node_modules/<name>/package.json للحزمة المثبّتة.
  6. حل البيان وحساب enabledFeatures:
    • [*]: جميع الميزات المعلنة (أو null إذا لم تكن هناك خريطة ميزات)
    • [a,b]: التحقق من وجود كل ميزة في خريطة ميزات البيان
    • []: قائمة ميزات فارغة
    • مواصفة مجردة: null (استخدام سياسة الافتراضيات لاحقاً في المُحمّل)
  7. تحديث أو إدراج حالة وقت تشغيل ملف القفل: { version, enabledFeatures, enabled: true }.

نظراً لأن التحديث مدفوع بالتثبيت:

  • xcsh plugin install pkg@newVersion يُحدّث التبعية وإصدار ملف القفل.
  • تُحفظ الإعدادات الحالية؛ يُستبدل إدخال الحالة للإصدار/الميزات/التفعيل.
  • لا توجد منطق “التحقق من التحديثات” أو الترحيل التحويلي.

تدفق الإزالة (PluginManager.uninstall)

Section titled “تدفق الإزالة (PluginManager.uninstall)”
  1. التحقق من اسم الحزمة.
  2. تشغيل bun uninstall <name> في مجلد الإضافة.
  3. إزالة حالة وقت تشغيل الإضافة من ملف القفل:
    • config.plugins[name]
    • config.settings[name]

إذا فشل أمر إلغاء التثبيت، لا تتغير حالة وقت التشغيل.

تدفق الإدراج (PluginManager.list)

Section titled “تدفق الإدراج (PluginManager.list)”
  1. قراءة خريطة تبعيات الإضافة من ~/.xcsh/plugins/package.json.
  2. تحميل تهيئة وقت تشغيل ملف القفل (الملف غير موجود -> افتراضيات فارغة).
  3. تحميل تجاوزات المشروع (<cwd>/.xcsh/plugin-overrides.json، أخطاء التحليل/القراءة -> كائن فارغ مع تحذير).
  4. لكل تبعية تحتوي على package.json قابل للحل:
    • بناء سجل InstalledPlugin
    • دمج حالة الميزة/التفعيل:
      • الأساس من ملف القفل (أو الافتراضيات)
      • يمكن لتجاوزات المشروع استبدال اختيار الميزات
      • قائمة disabled الخاصة بالمشروع تُخفي الإضافة كمعطّلة

هذه هي الحالة الفعّالة المستخدمة بواسطة مخرجات حالة CLI وعمليات الإعدادات/الميزات.

يدعم link تطوير الإضافات المحلية عبر إنشاء رابط رمزي لحزمة محلية في ~/.xcsh/plugins/node_modules/<pkg.name>.

السلوك:

  1. حل localPath بالنسبة لـ cwd المدير.
  2. طلب package.json المحلي وحقل name.
  3. التأكد من وجود مجلدات الإضافة.
  4. لأسماء النطاق، إنشاء مجلد النطاق.
  5. إزالة المسار الموجود في موقع الرابط المستهدف.
  6. إنشاء الرابط الرمزي.
  7. إضافة إدخال ملف القفل لوقت التشغيل ممكّناً بالميزات الافتراضية (null).

تنبيه: لا يُطبّق PluginManager.link الحالي فحص حدود مسار cwd الموجود في installer.ts القديم (normalizedPath.startsWith(normalizedCwd))، لذا تقع مسؤولية الثقة على عاتق المستدعي.

التحميل في وقت التشغيل: من الإضافة المثبّتة إلى القدرات القابلة للاستدعاء

Section titled “التحميل في وقت التشغيل: من الإضافة المثبّتة إلى القدرات القابلة للاستدعاء”

يقرأ getEnabledPlugins(cwd) (plugins/loader.ts):

  • بيان تبعيات الإضافة (package.json)
  • حالة وقت تشغيل ملف القفل
  • تجاوزات المشروع عبر getConfigDirPaths("plugin-overrides.json", { user: false, cwd })

التصفية:

  • تخطي إذا لم يوجد package.json للإضافة
  • تخطي إذا كان البيان (xcsh/pi) غائباً
  • تخطي إذا كان معطّلاً عالمياً في ملف القفل
  • تخطي إذا كان معطّلاً على مستوى المشروع

لكل إضافة ممكّنة:

  • resolvePluginToolPaths(plugin)
  • resolvePluginHookPaths(plugin)
  • resolvePluginCommandPaths(plugin)

يتضمن كل محلِّل إدخالات أساسية بالإضافة إلى إدخالات الميزات:

  • قائمة ميزات صريحة -> الميزات المحددة فقط
  • enabledFeatures === null -> تفعيل الميزات المميّزة بـ default: true

يتم تخطي الملفات المفقودة بصمت (حارس existsSync).

اختلافات التوصيل الحالي في وقت التشغيل

Section titled “اختلافات التوصيل الحالي في وقت التشغيل”
  • الأدوات مُوصَّلة في وقت التشغيل اليوم عبر discoverAndLoadCustomTools (custom-tools/loader.ts)، التي تستدعي getAllPluginToolPaths(cwd).
  • تُزال تكرارات المسارات بالمسار المطلق المحلول في اكتشاف الأدوات المخصصة (مجموعة seen، يفوز المسار الأول).
  • محلِّلات الخطافات/الأوامر موجودة ومُصدَّرة، لكن هذا المسار البرمجي لا يوصلها حالياً بسجل وقت تشغيل بنفس الطريقة التي تُوصَّل بها الأدوات.

تفاصيل إدارة القفل/الحالة

Section titled “تفاصيل إدارة القفل/الحالة”

يُخزّن PluginManager التهيئة في وقت التشغيل مؤقتاً في الذاكرة لكل مثيل (#runtimeConfig) ويُحمّلها بتكاسل مرة واحدة.

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

  • ملف القفل غير موجود -> { plugins: {}, settings: {} }
  • فشل قراءة/تحليل ملف القفل -> تحذير + نفس الافتراضيات الفارغة

سلوك الحفظ:

  • يكتب JSON كامل لملف القفل مُنسَّقاً عند كل تعديل

لا يوجد قفل متعدد العمليات أو استراتيجية دمج؛ يمكن للكتّاب المتزامنين الكتابة فوق بعضهم.

فحوصات الأمان وحدود الثقة

Section titled “فحوصات الأمان وحدود الثقة”

التحقق من المدخلات/الحزم

Section titled “التحقق من المدخلات/الحزم”

يُطبّق مسار المدير النشط التحقق من اسم الحزمة:

  • regex للمواصفات المحددة/غير المحددة النطاق (اختيارياً مع الإصدار)
  • قائمة رفض صريحة لأحرف shell الخاصة ([;&|$(){}[]<>\]`)

يُقيّد هذا مخاطر حقن الأوامر عند استدعاء bun install/uninstall.

  • يُنفَّذ كود الإضافة داخل العملية عند استيراد وحدات الأدوات المخصصة؛ لا يوجد عزل.
  • يُوصَّل المسارات النسبية للبيان بمجلد حزمة الإضافة ويُتحقق من وجودها فقط.
  • الحزمة نفسها تُعدّ كوداً موثوقاً به بعد التثبيت.

فحوصات المثبّت القديم فقط

Section titled “فحوصات المثبّت القديم فقط”

يتضمن installer.ts فحوصات إضافية عند وقت الربط غير موجودة في PluginManager.link:

  • يجب أن يُحلَّل المسار المحلي داخل cwd المشروع
  • حراس إضافية لاجتياز اسم الحزمة/المسار لتسمية الهدف الرمزي

نظراً لأن CLI يستخدم PluginManager، فإن هذه الحراس الأكثر صرامة للربط غير موجودة حالياً على المسار الرئيسي.

سلوك الفشل والنجاح الجزئي والتراجع

Section titled “سلوك الفشل والنجاح الجزئي والتراجع”

مدير الإضافات ليس تحويلياً.

مرحلة العمليةسلوك الفشلالتراجع
bun install يفشليتوقف التثبيت مع stderrغير متاح (لا كتابة للحالة بعد)
نجاح التثبيت، ثم فشل التحقق من البيان/الميزاتيفشل الأمرلا تراجع بإلغاء التثبيت؛ قد تبقى التبعية في node_modules/package.json
نجاح التثبيت، ثم فشل كتابة ملف القفليفشل الأمرلا تراجع للحزمة المثبّتة
نجاح bun uninstall، فشل كتابة ملف القفليفشل الأمرالحزمة مُزالة، قد تبقى حالة وقت تشغيل قديمة
link يُزيل الهدف القديم ثم يفشل إنشاء الرابط الرمزييفشل الأمرلا استعادة للرابط/المجلد السابق

تشغيلياً، يمكن لـ doctor --fix إصلاح بعض الانحرافات (bun install، تنظيف التهيئة اليتيمة، تنظيف الميزات غير الصالحة)، لكنه مجهود بذل ما في الوسع.

ملخص سلوك البيان المشوّه/المفقود

Section titled “ملخص سلوك البيان المشوّه/المفقود”
  • الحقل xcsh/pi مفقود:
    • التثبيت/الإدراج: مسموح به (بيان أدنى)
    • اكتشاف الإضافات الممكّنة في وقت التشغيل: يُتخطى كغير إضافة
  • ميزة مفقودة مشار إليها بمواصفة التثبيت أو features --set/--enable: خطأ صعب مع قائمة الميزات المتاحة
  • plugin-overrides.json غير صالح: يُتجاهل مع الرجوع إلى {} في كلا مسارات المدير والمُحمّل
  • مسارات الملفات للأدوات/الخطافات/الأوامر المشار إليها في البيان غير موجودة: يُتجاهل بصمت أثناء توسيع المحلِّل؛ يُشار إليها كأخطاء فقط بواسطة doctor

اختلافات الوضع والأولوية

Section titled “اختلافات الوضع والأولوية”
  • --dry-run (التثبيت): يُعيد نتيجة تثبيت اصطناعية، لا كتابة لنظام الملفات/الشبكة/الحالة.
  • --json: تنسيق المخرجات فقط، لا تغيير في السلوك.
  • تجاوزات المشروع تأخذ دائماً الأولوية على ملف القفل العام لعرض الميزات/الإعدادات.
  • التفعيل الفعّال هو runtimeEnabled && !projectDisabled.