با codex plugin marketplace add می‌شود یک ریشه‌ی محلی را به فهرست پلاگین‌های کدکس اضافه کرد و با codex plugin add از همان فهرست نصب کرد. در این پست یک مارکت‌پلیس سه‌فایلی ساخته شد، روی codex-cli 0.158.0 ثبت و نصب شد، و سه خطای محتمل با خروجی واقعی نشان داده شده است. لحظه ی خواندن: ۱۰ مهر ۱۴۰۵.

مارکت‌پلیس محلی دقیقا چه چیزی به کدکس اضافه می‌کند

کدکس پلاگین‌ها را از یک فهرست JSON به نام مارکت‌پلیس می‌خواند و همه‌ی فرمان‌های زیر codex plugin روی همان فهرست کار می‌کنند [1]. چهار زیر‌فرمانی که این نسخه واقعا دارد، add، list، marketplace و remove هستند.

# اول نسخه‌ی کلاینت و زیر‌فرمان‌های موجود را ببینید
$ codex --version
codex-cli 0.158.0
$ codex plugin --help | sed -n '/^Commands:/,/^Options:/p'
Commands:
  add          Install a plugin from a configured or remote marketplace
  list         List plugins available from configured and remote marketplaces
  marketplace  Add, list, upgrade, or remove configured marketplace sources
  remove       Uninstall a plugin and remove its local cache

خروجی codex plugin marketplace list روی این ماشین دو ریشه نشان داد: مارکت‌پلیس رسمی openai-api-curated که در مسیر /root/.codex/.tmp/plugins است، و هر ریشه‌ای که خودمان اضافه کنیم.

فایل .agents/plugins/api_marketplace.json در همان ریشه‌ی رسمی ۵۰ ورودی دارد و شمارش codex plugin list هم دقیقا ۵۰ ردیف با شناسه‌ی @openai-api-curated نشان داد. تفکیک همان ۵۰ ورودی روشن است: ۴۷ ورودی به یک مسیر محلی در همان ریشه اشاره می‌کنند و ۳ ورودی از یک آدرس گیت می‌آیند. دسته‌بندی‌های اعلام‌شده در همان فایل ۹ عنوان است، از Developer Tools تا Security [2].

سنجهمارکت‌پلیس رسمیمارکت‌پلیس محلی این پست
تعداد ورودی در فایل فهرست۵۰۱
منبع هر ورودی۴۷ محلی، ۳ گیت۱ محلی
محل بارگذاری پلاگینplugins/cache/plugins/cache/
نیاز به اعتبارنامهنداردندارد

سطر آخر مهم‌تر از بقیه است. ثبت یک مارکت‌پلیس محلی و نصب از آن هیچ تماسی با سرویس مدل نمی‌گیرد. روی این ماشین هیچ اعتبارنامه‌ای نصب نبود و codex exec با خطای ۴۰۱ رد شد، ولی هر چهار فرمان بالا بدون هیچ ورودی کار کردند.

اسکلت سه‌فایلی که یک پلاگین واقعی می‌سازد

یک مارکت‌پلیس محلی از سه فایل ساخته می‌شود: فایل فهرست، فایل مشخصات پلاگین و دست‌کم یک اسکیل. محل فایل فهرست در ریشه‌ای است که ثبت می‌کنید، نه در خود پوشه‌ی .agents/plugins [1]؛ این تفاوت را بخش خطاها نشان می‌دهد.

# فایل یک: فهرست پلاگین‌ها، در ریشه‌ای که ثبت می‌کنیم
.agents/plugins/marketplace.json
{
  "name": "hoosh-local",
  "interface": { "displayName": "Hoosh local" },
  "plugins": [
    {
      "name": "cost-guard",
      "source": { "source": "local", "path": "./plugins/cost-guard" },
      "policy": { "installation": "AVAILABLE", "authentication": "ON_INSTALL" },
      "category": "Developer Tools"
    }
  ]
}

# فایل دو: مشخصات پلاگین با نام، نسخه و نویسنده
plugins/cost-guard/.codex-plugin/plugin.json
{ "name": "cost-guard", "version": "1.0.0",
  "description": "Token accounting rules for agent sessions",
  "author": { "name": "Hoosh Dot Com" } }

# فایل سه: یک اسکیل با سرتیبل frontmatter
plugins/cost-guard/skills/cost-guard/SKILL.md
---
name: cost-guard
description: Use when a session gets expensive.
---

در هر ورودی، فیلد policy و فیلد category باید بیایند. مقدارهای مجاز policy.installation سه عنوان است: NOT_AVAILABLE، AVAILABLE و INSTALLED_BY_DEFAULT. برای policy.authentication هم دو مقدار ON_INSTALL و ON_USE پذیرفته می‌شود [2].

در سمت پلاگین، مسیرهای skills و hooks و mcpServers رشته‌ای نوشته می‌شوند که با ./ شروع شود و به ریشه‌ی پلاگین نسبت دارد [2]. وقتی پلاگینی چنین مسیری ندارد، کدکس پوشه‌های پیش‌فرض همان نام‌ها را خودش پیدا می‌کند، و برای همین پلاگین نمونه‌ی این پست فقط یک پوشه‌ی skills/ دارد و همان کافی بود.

ثبت، نصب و اثبات نصب با خروجی واقعی

ثبت ریشه و نصب پلاگین دو فرمان جدا هستند و هر کدام کد بازگشت خودش را دارند. اجرای پشت‌سرهم این چهار خط همان چیزی است که در ترمینال دیده شد.

$ find . -type f | sort
./.agents/plugins/marketplace.json
./plugins/cost-guard/.codex-plugin/plugin.json
./plugins/cost-guard/skills/cost-guard/SKILL.md

$ codex plugin marketplace add /root/.hermes/cache/scratch/hoosh-market
Added marketplace `hoosh-local` from /root/.hermes/cache/scratch/hoosh-market.
Installed marketplace root: /root/.hermes/cache/scratch/hoosh-market
exit=0

$ codex plugin add cost-guard@hoosh-local --json
{
  "pluginId": "cost-guard@hoosh-local",
  "name": "cost-guard",
  "marketplaceName": "hoosh-local",
  "version": "1.0.0",
  "installedPath": "/root/.codex/plugins/cache/hoosh-local/cost-guard/1.0.0",
  "authPolicy": "ON_INSTALL"
}
exit=0

خروجی JSON دو چیز را ثابت می‌کند که خروجی متنی نشان نمی‌دهد: مسیر نصب، و اینکه نسخه‌ی پلاگین از فایل مشخصات خوانده شده است. همان سوییچ --json روی فرمان ثبت مارکت‌پلیس هم کار می‌کند و فیلد alreadyAdded را برمی‌گرداند، پس دوباره ثبت کردن همان ریشه خطا نمی‌دهد.

$ codex plugin list | grep cost-guard
cost-guard@hoosh-local  installed, enabled  1.0.0    /root/.hermes/cache/scratch/hoosh-market/plugins/cost-guard

$ codex plugin marketplace list --json
{
  "marketplaces": [
    { "name": "openai-api-curated", "root": "/root/.codex/.tmp/plugins" },
    { "name": "hoosh-local",
      "root": "/root/.hermes/cache/scratch/hoosh-market",
      "marketplaceSource": { "sourceType": "local",
        "source": "/root/.hermes/cache/scratch/hoosh-market" } }
  ]
}

قاعده‌ی کاربردی این پست همین‌جا جمع می‌شود: ستون وضعیت installed, enabled تنها اثبات نصب است، و ستون آخر همیشه مسیر منبع را نشان می‌دهد نه مسیر بارگذاری. اگر این دو را یکی بگیرید، بعد از حذف پلاگین فکر می‌کنید هنوز نصب است.

سه خطایی که در عمل رخ دادند

خطای اول همان چیزی است که تقریبا هر کسی در بار اول می‌نویسد: گذاشتن فایل فهرست در ریشه. خطای دوم نبودن فایل مشخصات پلاگین است، و خطای سوم آن است که یک کلید ناشناخته در مارکت‌پلیس اصلا خطا نمی‌دارد.

# خطای یک: فایل فهرست در ریشه به جای .agents/plugins
$ codex plugin marketplace add .../hoosh-bad/root-manifest
Error: invalid marketplace file `.../root-manifest`: marketplace root does not contain a supported manifest
exit=1

# خطای دو: پلاگین بدون فایل مشخصات؛ ثبت موفق، نصب ناموفق
$ codex plugin add ghost@no-manifest --json
Error: missing plugin.json

Caused by:
    missing plugin.json
exit=1

# خطای سه: کلید ناشناخته در ورودی مارکت‌پلیس بی‌صدا نادیده گرفته می‌شود
$ codex plugin add p1@unknown-key --json
{ "pluginId": "p1@unknown-key", "version": "1.0.0",
  "installedPath": "/root/.codex/plugins/cache/unknown-key/p1/1.0.0" }
exit=0

معنای هر کدام جداگانه فرق دارد. خطای یک را خود کدکس می‌گیرد، پس ریشه‌ی اشتباه قبل از هر کار دیگری پیدا می‌شود. خطای دو نشان می‌دهد ثبت مارکت‌پلیس محتوای پلاگین‌ها را اعتبارسنجی نمی‌کند و فقط موقع نصب جلوی کار را می‌گیرد. خطای سوم از همه مهم‌تر است: کلیدی مثل totallyUnknownKey در ورودی مارکت‌پلیس بی‌صدا نادیده گرفته می‌شود و پلاگین نصب می‌شود.

همین رفتار در کلاد کد برعکس است. طبق مرجع فیلدهای پلاگین کلاد کد، یک کلید ناشناس در سطح بالای فایل حذف و پلاگین بارگذاری می‌شود و claude plugin validate برای آن هشدار می‌دهد، ولی یک کلید ناشناس درون شیء‌های سخت‌گیر مثل userConfig خطاست و پلاگین بارگذاری نمی‌شود [6].

دو محدودیت دیگر هم در همین خانواده است. نام یک مارکت‌پلیس نمی‌تواند از دو ریشه استفاده کند؛ تلاش برای ثبت ریشه‌ای دیگر با همان نام hoosh-local رد شد و کدکس خواسته است اول ریشه‌ی قبلی حذف شود. نام هر ورودی هم باید با نام پوشه‌ی پلاگین یکی باشد [2].

کدکس پلاگین نصب‌شده را از کجا می‌خواند

پلاگین از ریشه‌ی مارکت‌پلیس اجرا نمی‌شود. یک کپی در مسیر plugins/cache/ زیر خانه‌ی کدکس قرار می‌گیرد و همین کپی بارگذاری می‌شود [1]. بررسی پوشه‌ی کش بعد از نصب نشان داد که هر دو فایل پلاگین آنجا هستند و فایل دیگری کنارشان نیست.

$ find /root/.codex/plugins/cache/nover -type f | sort
/root/.codex/plugins/cache/nover/nv/local/.codex-plugin/plugin.json
/root/.codex/plugins/cache/nover/nv/local/skills/nv/SKILL.md

پوشه‌ی local در مسیر بالا تصادفی نیست. وقتی فایل مشخصات پلاگین فیلد version نداشته باشد، کدکس نسخه را همان local می‌گذارد و همان را در خروجی JSON هم چاپ می‌کند. یعنی نسخه‌ی نداده‌شده یک استثنا نیست، یک مقدار مشخص است.

خود ثبت هم روی دیسک می‌نشیند. فایل ~/.codex/config.toml دو خط تازه گرفت که همان چیزی است که مرجع پیکربندی کدکس توصیف می‌کند [4]، و روزی که مارکت‌پلیس حذف شد همان دو خط هم پاک شدند.

$ codex plugin marketplace remove hoosh-local
Removed marketplace `hoosh-local`.
exit=0

$ tail -n 6 /root/.codex/config.toml
[projects."/root/.hermes/cache/scratch/ws-demo"]
trust_level = "trusted"

[plugins."plugin-eval@openai-api-curated"]
enabled = true

پاک کردن پلاگین هم یک مرحله‌ی جدا دارد و ترتیبشان مهم است: اول پلاگین، بعد مارکت‌پلیس. پس از حذف پلاگین، پوشه‌ی کش خالی بود ولی ردیف آن در فهرست باقی ماند و وضعیتش not installed شد، چون هنوز در فایل فهرست معرفی شده است.

همان ریشه‌ی رسمی هم یک نکته‌ی عملی دارد: در آن مخزن فایل .agents/plugins/marketplace.json با نام openai-curated و ۶۵ ورودی وجود دارد، ولی این نسخه از کدکس به جای آن فایل api_marketplace.json را برداشت. برای همین شمارش ۵۰ است و نه ۶۵.

کلاد کد همین مسیر را از قبل باز کرده است

اگر پلاگین خودتان را برای هر دو ابزار می‌خواهید، نام پوشه‌ها فرق می‌کند و همین یک دلیل کافی است که دو ریپو جدا نگه دارید. کدکس دنبال .codex-plugin/plugin.json می‌گردد و فایل فهرست را از .agents/plugins/marketplace.json می‌خواند، در حالی که کلاد کد دنبال .claude-plugin/plugin.json است [8].

آن طرف، نسخه‌های اخیر کلاد کد مسیر توسعه‌ی محلی را باز کرده‌اند. نسخه‌ی 2.1.265 منتشرشده در ۱۸ شهریور، پرچم --plugin-dir را طوری عوض کرد که یک پوشه‌ی حاوی چند پلاگین را بگیرد و هر زیرپوشه‌ای که مشخصات داشته باشد بارگذاری شود [9]. همین پرچم برای آزمون محلی هم توصیه شده است [7].

نسخه‌ی 2.1.281 در ۳۲ شهریور همان مسیر را درست کرد، چون روی پوشه‌ای که خودش فایل فهرست داشت به جای پلاگین‌ها یک پلاگین خالی بارگذاری می‌شد [10]. نسخه‌ی 2.1.283 در ۴ مهر یک چیز قابل اندازه‌گیری اضافه کرد: در رویداد system/init با خروجی stream-json، هر خطای بارگذاری --plugin-dir حالا فیلد path هم دارد و نام همان پوشه‌ی ناموفق را چاپ می‌کند [11].

آنچه در این پست اندازه نگرفته‌ام: هیچ نشست مدلی اجرا نشد، چون اعتبارنامه‌ای روی این ماشین نبود و codex exec با کد ۴۰۱ برگشت. اثر واقعی یک اسکیل روی کیفیت کار ایجنت در این پست سنجیده نشده است؛ تنها چیزی که سنجیده شد زنجیره‌ی نصب است. برای سنجیدن هزینه‌ی یک اسکیل واقعی، پست سنجش هزینه‌ی توکن یک اسکیل با plugin-eval مسیر درست بعدی است، و برای دیدن اینکه چه پرچم‌هایی در این نسخه روشن‌اند، خواندن وضعیت واقعی پرچم‌های کدکس پیش از هر آزمونی لازم است.

منابع

  1. ساخت پلاگین در کدکس: فرم‌های codex plugin marketplace add، محل فایل فهرست و مسیر کش — خوانده در ۱۰ مهر ۱۴۰۵
  2. بسته‌بندی پلاگین: فیلدهای plugin.json، قالب policy، قواعد مسیر و قواعد تولید مارکت‌پلیس — خوانده در ۱۰ مهر ۱۴۰۵
  3. مستندات پلاگین‌ها: اجزای یک پلاگین و اینکه کدام بخش در کدام محصول کار می‌کند — خوانده در ۱۰ مهر ۱۴۰۵
  4. مرجع پیکربندی کدکس: کلیدهای config.toml و دامنه‌ی هر کدام — خوانده در ۱۰ مهر ۱۴۰۵
  5. مرجع فرمان‌های پلاگین کلاد کد: claude plugin و پرچم‌های بارگذاری یک نشست — خوانده در ۱۰ مهر ۱۴۰۵
  6. مرجع مشخصات پلاگین کلاد کد: رفتار کلید ناشناس در سطح بالا و در شیء‌های سخت‌گیر — خوانده در ۱۰ مهر ۱۴۰۵
  7. ساخت پلاگین در کلاد کد: چیدمان پوشه‌ها و آزمون محلی با --plugin-dir — خوانده در ۱۰ مهر ۱۴۰۵
  8. ساخت و آزمون پلاگین کلاد کد: تفاوت پوشه‌ی .claude-plugin با پوشه‌های اجزا — خوانده در ۱۰ مهر ۱۴۰۵
  9. انتشار v2.1.265 در ۱۸ شهریور: پشتیبانی --plugin-dir از یک پوشه‌ی پلاگین — خوانده در ۱۰ مهر ۱۴۰۵
  10. انتشار v2.1.281 در ۳۲ شهریور: اصلاح بارگذاری یک پلاگین خالی از پوشه‌ی پلاگین‌ها — خوانده در ۱۰ مهر ۱۴۰۵
  11. انتشار v2.1.283 در ۴ مهر: افزودن فیلد path به خطاهای بارگذاری در stream-json — خوانده در ۱۰ مهر ۱۴۰۵
  12. فایل CHANGELOG.md کلاد کد روی گیت‌هاب، منبع اصلی سطرهای تغییرات — خوانده در ۱۰ مهر ۱۴۰۵
  13. انتشار rust-v0.160.0 در ۱۱ مهر: کش کردن فایل‌های مشخصات پلاگین در کارهای پس‌زمینه — خوانده در ۱۰ مهر ۱۴۰۵