---
read_when:
    - افزودن/تغییر نقاط پایانی
    - اشکال‌زدایی درخواست‌های CLI ↔ رجیستری
summary: مرجع API مبتنی بر HTTP (عمومی + نقاط پایانی CLI + احراز هویت).
x-i18n:
    generated_at: "2026-07-27T13:46:35Z"
    model: gpt-5.6
    postprocess_version: locale-links-v1
    prompt_version: 32
    provider: openai
    source_hash: 5b180bbd56d20a3d88c1fe74ccab0fd0ecbe0e8c9624cd1afd2070a2ca1f7fb3
    source_path: clawhub/http-api.md
    workflow: 16
---

# API HTTP

نشانی پایه: `https://clawhub.ai` (پیش‌فرض).

همه مسیرهای v1 زیرمجموعه `/api/v1/...` هستند.
مسیرهای قدیمی `/api/...` و `/api/cli/...` برای سازگاری باقی مانده‌اند (به `DEPRECATIONS.md` مراجعه کنید).
OpenAPI: `/api/v1/openapi.json`.

## استفاده مجدد از کاتالوگ عمومی

دایرکتوری‌های شخص ثالث می‌توانند از نقطه‌های پایانی عمومی خواندن برای فهرست‌کردن یا جست‌وجوی Skills در ClawHub استفاده کنند. لطفاً نتایج را کش کنید، `429`/`Retry-After` را رعایت کنید، کاربران را به فهرست مرجع ClawHub (`https://clawhub.ai/<owner>/skills/<slug>`) پیوند دهید و از القای تأیید سایت شخص ثالث توسط ClawHub خودداری کنید. برای بازتاب‌دادن محتوای پنهان، خصوصی یا مسدودشده توسط نظارت در خارج از سطح API عمومی تلاش نکنید.

میان‌برهای نامک وب در خانواده‌های مختلف رجیستری تفکیک می‌شوند، اما کلاینت‌های API باید به‌جای بازسازی تقدم مسیرها، از URLهای مرجعی استفاده کنند که نقطه‌های پایانی خواندن برمی‌گردانند.

## محدودیت‌های نرخ

مدل اعمال محدودیت:

- درخواست‌های ناشناس: به‌ازای هر IP اعمال می‌شود.
- درخواست‌های احراز هویت‌شده (توکن Bearer معتبر): به‌ازای سبد هر کاربر اعمال می‌شود.
- اگر توکن موجود نباشد یا نامعتبر باشد، رفتار به اعمال محدودیت بر پایه IP بازمی‌گردد.
- نقطه‌های پایانی نوشتن احراز هویت‌شده، هنگامی که
  سرور دلیل را می‌داند، نباید فقط یک `Unauthorized` برگردانند. توکن‌های مفقود، توکن‌های نامعتبر/لغوشده و
  حساب‌های حذف‌شده/مسدودشده/غیرفعال باید هرکدام متنی عملیاتی دریافت کنند تا کلاینت‌های
  CLI بتوانند به کاربران بگویند چه چیزی مانع آن‌ها شده است.

- خواندن: 3000/min به‌ازای هر IP، ‏12000/min به‌ازای هر کلید
- نوشتن: 300/min به‌ازای هر IP، ‏3000/min به‌ازای هر کلید
- دانلود: 1200/min به‌ازای هر IP، ‏6000/min به‌ازای هر کلید (نقطه‌های پایانی دانلود)

سرآیندها:

- سازگاری قدیمی: `X-RateLimit-Limit`، `X-RateLimit-Reset`
- استانداردشده: `RateLimit-Limit`، `RateLimit-Reset`
- در `429`:‏ `X-RateLimit-Remaining: 0` و `RateLimit-Remaining: 0`
- در `429`:‏ `Retry-After`

معنای سرآیندها:

- `X-RateLimit-Reset`: ثانیه‌های مطلق دوره Unix
- `RateLimit-Reset`: تعداد ثانیه‌ها تا بازنشانی (تأخیر)
- `X-RateLimit-Remaining` / `RateLimit-Remaining`: بودجه دقیق باقی‌مانده، در صورت وجود.
  درخواست‌های موفق شاردشده به‌جای برگرداندن یک مقدار تقریبی سراسری، این سرآیند را حذف می‌کنند.
- `Retry-After`: تعداد ثانیه‌های انتظار پیش از تلاش مجدد (تأخیر) در `429`

نمونه پاسخ `429`:

```http
HTTP/2 429
content-type: text/plain; charset=utf-8
x-ratelimit-limit: 20
x-ratelimit-remaining: 0
x-ratelimit-reset: 1771404540
ratelimit-limit: 20
ratelimit-remaining: 0
ratelimit-reset: 34
retry-after: 34

از محدودیت نرخ فراتر رفته است
```

راهنمای کلاینت:

- اگر `Retry-After` وجود دارد، پیش از تلاش مجدد به همان تعداد ثانیه منتظر بمانید.
- برای جلوگیری از تلاش‌های مجدد هم‌زمان، از پس‌روی همراه با نوسان تصادفی استفاده کنید.
- اگر `Retry-After` موجود نیست، به `RateLimit-Reset` بازگردید (یا آن را از `X-RateLimit-Reset` محاسبه کنید).

منبع IP:

- تنها زمانی از سرآیندهای مورد اعتماد IP کلاینت، از جمله `cf-connecting-ip`، استفاده می‌کند که
  استقرار به‌طور صریح سرآیندهای ارسال‌شده مورد اعتماد را فعال کرده باشد.
- ClawHub برای شناسایی IP کلاینت‌ها در لبه از سرآیندهای ارسال مورد اعتماد استفاده می‌کند.
- اگر هیچ IP مورد اعتماد کلاینت در دسترس نباشد، درخواست‌های ناشناس از سبدهای جایگزینی استفاده می‌کنند
  که فقط بر اساس نوع محدودیت نرخ دامنه‌بندی شده‌اند. این سبدهای جایگزین شامل
  مسیرها، نامک‌ها، نام بسته‌ها، نسخه‌ها، رشته‌های پرس‌وجو یا سایر
  پارامترهای مصنوع ارائه‌شده توسط فراخواننده نیستند.

## پاسخ‌های خطا

پاسخ‌های خطای عمومی v1 متنی ساده با `content-type: text/plain; charset=utf-8` هستند.
این شامل شکست‌های اعتبارسنجی (`400`)، منابع عمومی مفقود (`404`)، شکست‌های احراز هویت و
مجوز (`401`/`403`)، محدودیت‌های نرخ (`429`) و دانلودهای مسدودشده است. کلاینت‌ها
باید بدنه پاسخ را به‌صورت رشته‌ای خوانا برای انسان بخوانند. پارامترهای پرس‌وجوی ناشناخته برای
سازگاری نادیده گرفته می‌شوند، اما پارامترهای پرس‌وجوی شناخته‌شده با مقادیر نامعتبر
`400` را برمی‌گردانند.

## نقطه‌های پایانی عمومی (بدون احراز هویت)

### `GET /api/v1/search`

پارامترهای پرس‌وجو:

- `q` (الزامی): رشته پرس‌وجو
- `limit` (اختیاری): عدد صحیح
- `highlightedOnly` (اختیاری): `true` برای محدودکردن نتایج به Skills برجسته
- `nonSuspiciousOnly` (اختیاری): `true` برای پنهان‌کردن Skills مشکوک (`flagged.suspicious`)
- `nonSuspicious` (اختیاری): نام مستعار قدیمی برای `nonSuspiciousOnly`

پاسخ:

```json
{
  "results": [
    {
      "score": 0.123,
      "slug": "gifgrep",
      "displayName": "GifGrep",
      "summary": "…",
      "version": "1.2.3",
      "updatedAt": 1730000000000,
      "ownerHandle": "openclaw",
      "owner": {
        "handle": "openclaw",
        "displayName": "OpenClaw",
        "image": "https://example.com/avatar.png"
      }
    }
  ]
}
```

نکته‌ها:

- نتایج به‌ترتیب ارتباط برگردانده می‌شوند (شباهت تعبیه‌سازی + تقویت تطابق دقیق توکن نامک/نام + یک پیشین محبوبیت کوچک).
- ارتباط از محبوبیت تأثیر بیشتری دارد. تطابق دقیق توکن نامک یا نام نمایشی می‌تواند بالاتر از تطابقی ضعیف‌تر با تعامل بسیار بیشتر قرار گیرد.
- متن ASCII در مرزهای واژه و علائم نگارشی توکن‌سازی می‌شود. برای نمونه، `personal-map` دارای یک توکن مستقل `map` است، درحالی‌که `amap-jsapi-skill` شامل `amap`، `jsapi` و `skill` است؛ بنابراین جست‌وجوی `map` برای `personal-map` تطابق واژگانی قوی‌تری نسبت به `amap-jsapi-skill` ایجاد می‌کند.
- محبوبیت با مقیاس لگاریتمی محاسبه و سقف‌گذاری می‌شود. Skills با تعامل بالا ممکن است وقتی متن پرس‌وجو تطابق ضعیف‌تری دارد، رتبه پایین‌تری بگیرند.
- وضعیت نظارتی مشکوک یا پنهان می‌تواند بسته به فیلترهای فراخواننده و وضعیت فعلی نظارت، یک Skill را از جست‌وجوی عمومی حذف کند.

راهنمای قابلیت کشف برای ناشر:

- عبارت‌هایی را که کاربران عیناً جست‌وجو می‌کنند در نام نمایشی، خلاصه و برچسب‌ها قرار دهید. تنها زمانی از یک توکن مستقل نامک استفاده کنید که هویت پایداری نیز باشد که می‌خواهید حفظ کنید.
- نامک را صرفاً برای دنبال‌کردن یک پرس‌وجو تغییر ندهید، مگر اینکه نامک جدید نام مرجع بلندمدت بهتری باشد. نامک‌های قدیمی به نام‌های مستعار تغییرمسیر تبدیل می‌شوند، اما URL مرجع، نامک نمایش‌داده‌شده و خلاصه‌های جست‌وجوی آینده از نامک جدید استفاده می‌کنند.
- نام‌های مستعار تغییرنام، تفکیک را برای URLهای قدیمی و نصب‌هایی که از طریق رجیستری تفکیک می‌شوند حفظ می‌کنند، اما رتبه‌بندی جست‌وجو پس از نمایه‌شدن تغییرنام بر اساس فراداده مرجع Skill انجام می‌شود. آمار موجود همراه Skill باقی می‌ماند.
- اگر یک Skill به‌طور غیرمنتظره نامرئی است، پیش از تغییر فراداده مرتبط با رتبه‌بندی، ابتدا در حالت ورود به سیستم وضعیت نظارت را با `clawhub inspect @owner/slug` بررسی کنید.

### `GET /api/v1/skills`

پارامترهای پرس‌وجو:

- `limit` (اختیاری): عدد صحیح (1–200)
- `cursor` (اختیاری): مکان‌نمای صفحه‌بندی برای هر مرتب‌سازی غیر از `trending`
- `sort` (اختیاری): `updated` (پیش‌فرض)، `recommended` (نام مستعار: `default`)، `createdAt` (نام مستعار: `newest`)، `downloads`، `stars` (نام مستعار: `rating`)؛ نام‌های مستعار قدیمی نصب `installsCurrent`/`installs`/`installsAllTime` به `downloads`، `trending` نگاشت می‌شوند
- `nonSuspiciousOnly` (اختیاری): `true` برای پنهان‌کردن Skills مشکوک (`flagged.suspicious`)
- `nonSuspicious` (اختیاری): نام مستعار قدیمی برای `nonSuspiciousOnly`

مقادیر نامعتبر `sort`، مقدار `400` را برمی‌گردانند.

نکته‌ها:

- `recommended` از سیگنال‌های تعامل و تازگی استفاده می‌کند.
- `trending` بر اساس نصب‌ها در 7 روز گذشته رتبه‌بندی می‌کند (بر پایه تله‌متری).
- `createdAt` برای خزش Skills جدید پایدار است؛ `updated` هنگام انتشار مجدد Skills موجود تغییر می‌کند.
- هنگامی که `nonSuspiciousOnly=true`، مرتب‌سازی‌های مبتنی بر مکان‌نما ممکن است کمتر از `limit` مورد در یک صفحه برگردانند، زیرا Skills مشکوک پس از بازیابی صفحه فیلتر می‌شوند.
- در صورت وجود، از `nextCursor` برای ادامه صفحه‌بندی استفاده کنید. کوتاه‌بودن یک صفحه به‌تنهایی به معنای پایان نتایج نیست.

پاسخ:

```json
{
  "items": [
    {
      "slug": "gifgrep",
      "displayName": "GifGrep",
      "summary": "…",
      "topics": ["Productivity"],
      "tags": { "latest": "1.2.3" },
      "stats": {},
      "createdAt": 0,
      "updatedAt": 0,
      "latestVersion": { "version": "1.2.3", "createdAt": 0, "changelog": "…" },
      "metadata": { "os": ["macos"], "systems": ["aarch64-darwin"] }
    }
  ],
  "nextCursor": null
}
```

### `GET /api/v1/skills/{slug}`

پاسخ:

```json
{
  "skill": {
    "slug": "gifgrep",
    "displayName": "GifGrep",
    "summary": "…",
    "topics": ["Productivity"],
    "tags": { "latest": "1.2.3" },
    "stats": {},
    "createdAt": 0,
    "updatedAt": 0
  },
  "latestVersion": { "version": "1.2.3", "createdAt": 0, "changelog": "…" },
  "metadata": { "os": ["macos"], "systems": ["aarch64-darwin"] },
  "owner": { "handle": "steipete", "displayName": "Peter", "image": null },
  "moderation": {
    "isSuspicious": false,
    "isMalwareBlocked": false,
    "verdict": "clean",
    "reasonCodes": [],
    "summary": null,
    "engineVersion": "v2.0.0",
    "updatedAt": 0
  }
}
```

نکته‌ها:

- نامک‌های قدیمی ایجادشده توسط جریان‌های تغییرنام/ادغام مالک، به Skill مرجع تفکیک می‌شوند.
- `metadata.os`: محدودیت‌های سیستم‌عامل اعلام‌شده در فرانت‌متر Skill (برای نمونه `["macos"]`، `["linux"]`). در صورت اعلام‌نشدن، `null`.
- `metadata.systems`: اهداف سیستم Nix (برای نمونه `["aarch64-darwin", "x86_64-linux"]`). در صورت اعلام‌نشدن، `null`.
- اگر Skill هیچ فراداده پلتفرمی نداشته باشد، `metadata` برابر با `null` است.
- `moderation` فقط زمانی گنجانده می‌شود که Skill پرچم‌گذاری شده باشد یا مالک آن را مشاهده کند.

### `GET /api/v1/skills/{slug}/moderation`

وضعیت ساخت‌یافته نظارت را برمی‌گرداند.

پاسخ:

```json
{
  "moderation": {
    "isSuspicious": true,
    "isMalwareBlocked": false,
    "verdict": "suspicious",
    "reasonCodes": ["suspicious.dynamic_code_execution"],
    "summary": "Detected: suspicious.dynamic_code_execution",
    "engineVersion": "v2.0.0",
    "updatedAt": 0,
    "legacyReason": null,
    "evidence": [
      {
        "code": "suspicious.dynamic_code_execution",
        "severity": "critical",
        "file": "index.ts",
        "line": 3,
        "message": "Dynamic code execution detected.",
        "evidence": ""
      }
    ]
  }
}
```

نکته‌ها:

- مالکان و ناظران می‌توانند به جزئیات نظارت برای Skills پنهان دسترسی داشته باشند.
- فراخوانندگان عمومی فقط برای Skills قابل مشاهده‌ای که از قبل پرچم‌گذاری شده‌اند، `200` را دریافت می‌کنند.
- شواهد برای فراخوانندگان عمومی ویرایش می‌شوند و قطعه‌های خام فقط برای مالکان/ناظران گنجانده می‌شوند.

### `POST /api/v1/skills/{slug}/report`

یک Skill را برای بازبینی ناظر گزارش کنید. گزارش‌ها در سطح Skill هستند، می‌توانند به‌صورت اختیاری
به یک نسخه پیوند داده شوند و وارد صف گزارش Skill می‌شوند.

احراز هویت:

- به یک توکن API نیاز دارد.

درخواست:

```json
{ "reason": "Suspicious install step", "version": "1.2.3" }
```

پاسخ:

```json
{
  "ok": true,
  "reported": true,
  "alreadyReported": false,
  "reportId": "skillReports:...",
  "skillId": "skills:...",
  "reportCount": 1
}
```

### `GET /api/v1/skills/-/reports`

نقطه پایانی ناظر/مدیر برای دریافت گزارش‌های Skill.

پارامترهای پرس‌وجو:

- `status` (اختیاری): `open` (پیش‌فرض)، `confirmed`، `dismissed` یا `all`
- `limit` (اختیاری): عدد صحیح (1-200)
- `cursor` (اختیاری): مکان‌نمای صفحه‌بندی

پاسخ:

```json
{
  "items": [
    {
      "reportId": "skillReports:...",
      "skillId": "skills:...",
      "skillVersionId": "skillVersions:...",
      "slug": "gifgrep",
      "displayName": "GifGrep",
      "version": "1.2.3",
      "reason": "مرحله نصب مشکوک",
      "status": "open",
      "createdAt": 1730000000000,
      "reporter": {
        "userId": "users:...",
        "handle": "reporter",
        "displayName": "گزارش‌دهنده"
      },
      "triagedAt": null,
      "triagedBy": null,
      "triageNote": null
    }
  ],
  "nextCursor": null,
  "done": true
}
```

### `POST /api/v1/skills/-/reports/{reportId}/triage`

نقطه پایانی مدیر/ناظر برای حل‌وفصل یا بازگشایی گزارش‌های Skills.

درخواست:

```json
{ "status": "confirmed", "note": "بازبینی شد و نسخه تحت‌تأثیر پنهان شد.", "finalAction": "hide" }
```

`note` برای `confirmed` و `dismissed` الزامی است؛ هنگام
بازگرداندن `status` به `open` می‌توان آن را حذف کرد. برای پنهان‌کردن Skill در همان گردش‌کار قابل‌حسابرسی،
`finalAction: "hide"` را همراه یک گزارش بررسی‌شده ارسال کنید.

### `GET /api/v1/skills/{slug}/versions`

پارامترهای پرس‌وجو:

- `limit` (اختیاری): عدد صحیح
- `cursor` (اختیاری): مکان‌نمای صفحه‌بندی

### `GET /api/v1/skills/{slug}/versions/{version}`

فراداده نسخه + فهرست فایل‌ها را برمی‌گرداند.

- `version.security` در صورت موجودبودن، شامل وضعیت نرمال‌شده تأیید اسکن و جزئیات اسکنر
  (VirusTotal + LLM) است.

### `GET /api/v1/skills/{slug}/scan`

جزئیات تأیید اسکن امنیتی یک نسخه Skill را برمی‌گرداند.

پارامترهای پرس‌وجو:

- `version` (اختیاری): رشته نسخه مشخص.
- `tag` (اختیاری): برطرف‌کردن یک نسخه برچسب‌خورده (برای مثال `latest`).

نکته‌ها:

- اگر نه `version` و نه `tag` ارائه شده باشد، از آخرین نسخه استفاده می‌کند.
- شامل وضعیت نرمال‌شده تأیید و جزئیات مختص هر اسکنر است.
- `security.hasScanResult` فقط زمانی `true` است که یک اسکنر حکمی قطعی تولید کرده باشد (`clean`، `suspicious` یا `malicious`).
- `moderation` یک نمای لحظه‌ای فعلی از نظارت در سطح Skill است که از آخرین نسخه مشتق می‌شود.
- هنگام پرس‌وجوی یک نسخه تاریخی، پیش از یکسان درنظرگرفتن زمینه نسخه `moderation` و `security`، `moderation.matchesRequestedVersion` و `moderation.sourceVersion` را بررسی کنید.

### `POST /api/v1/skills/-/scan`

نقطه پایانی ارسال احراز هویت‌شده برای کارهای جدید ClawScan.

اسکن بارگذاری محلی دیگر پشتیبانی نمی‌شود. درخواست‌هایی که از
`multipart/form-data` یا `{ "source": { "kind": "upload" } }` استفاده می‌کنند، `410` را برمی‌گردانند.

اسکن‌های منتشرشده از JSON استفاده می‌کنند:

```json
{
  "source": { "kind": "published", "slug": "gifgrep", "version": "1.2.3" },
  "update": false
}
```

نکته‌ها:

- محموله‌های درخواست اسکن و گزارش‌های قابل‌دریافت، پس از دوره نگه‌داری از مخزن درخواست اسکن منقضی می‌شوند.
- اسکن‌های منتشرشده به دسترسی مدیریتی مالک/ناشر یا اختیار ناظر/مدیر پلتفرم نیاز دارند.
- اسکن‌های منتشرشده فقط زمانی بازنویسی می‌شوند که `update: true` باشد و اسکن با موفقیت کامل شود.
- پاسخ، `202` همراه با `{ "ok": true, "scanId": "...", "jobId": "...", "status": "queued", "sourceKind": "published", "update": false, "queue": { "queuedAhead": 0, "queuedAheadIsEstimate": false, "position": 1, "running": 0, "runningIsEstimate": false, "note": "Scans are asynchronous and may take time to complete." } }` است.
- کارهای اسکن ناهمگام هستند. درخواست‌های اسکن دستی جلوتر از کارهای عادی انتشار/تکمیل سوابق اولویت‌بندی می‌شوند، اما تکمیل همچنان به دردسترس‌بودن worker بستگی دارد.

### `GET /api/v1/skills/-/scan/{scanId}`

نقطه پایانی نظرسنجی احراز هویت‌شده برای یک اسکن ارسال‌شده.

- وضعیت در صف/در حال اجرا/موفق/ناموفق را برمی‌گرداند.
- در مدت حضور در صف، `queue.queuedAhead` و `queue.position` را برمی‌گرداند تا کلاینت‌ها بتوانند تعداد اسکن‌های دستی اولویت‌داری را که جلوتر از درخواست هستند نمایش دهند. صف‌های بسیار بزرگ محدود شده و با `queuedAheadIsEstimate: true` گزارش می‌شوند.
- در صورت موجودبودن، `report` شامل بخش‌های `clawscan`، `skillspector`، `staticAnalysis` و `virustotal` است.
- کارهای اسکن ناموفق، `status: "failed"` را همراه با `lastError` برمی‌گردانند.

### `GET /api/v1/skills/-/scan/{scanId}/download`

نقطه پایانی بایگانی گزارش احراز هویت‌شده.

- به یک اسکن موفق نیاز دارد؛ اسکن‌های غیرنهایی `409` را برمی‌گردانند.
- یک فایل ZIP شامل `manifest.json`، `clawscan.json`، `skillspector.json`، `static-analysis.json`، `virustotal.json` و `README.md` برمی‌گرداند.

### `GET /api/v1/skills/-/scan/download/{name}?version=<version>&kind=skill|plugin`

نقطه پایانی احراز هویت‌شده بایگانی گزارش ذخیره‌شده برای نسخه‌های ارسال‌شده.

- به دسترسی مدیریتی مالک/ناشر Skill یا Plugin، یا اختیار ناظر/مدیر پلتفرم نیاز دارد.
- نتایج اسکن ذخیره‌شده برای نسخه دقیق ارسال‌شده، از جمله نسخه‌های مسدود یا پنهان، را برمی‌گرداند.
- مقدار پیش‌فرض `kind` برابر `skill` است؛ برای اسکن‌های Plugin/بسته از `kind=plugin` استفاده کنید.
- همان ساختار ZIP دریافت‌های درخواست اسکن را برمی‌گرداند.

### `POST /api/v1/skills/-/scan/batch`

مسیر متعارف بازاسکن دسته‌ای ویژه مدیر. این مسیر همان ساختار محموله `POST /api/v1/skills/-/rescan-batch` قدیمی را می‌پذیرد.

### `POST /api/v1/skills/-/scan/batch/status`

مسیر متعارف وضعیت دسته‌ای ویژه مدیر. این مسیر `{ "jobIds": ["..."] }` را می‌پذیرد و همان شمارنده‌های تجمیعی `POST /api/v1/skills/-/rescan-batch/status` قدیمی را برمی‌گرداند.

### `GET /api/v1/skills/{slug}/verify`

پاکت تأیید Skill Card مورداستفاده `clawhub skill verify` را برمی‌گرداند.

پارامترهای پرس‌وجو:

- `version` (اختیاری): رشته نسخه مشخص.
- `tag` (اختیاری): برطرف‌کردن یک نسخه برچسب‌خورده (برای مثال `latest`).

نکته‌ها:

- `ok` فقط زمانی `true` است که نسخه انتخاب‌شده دارای Skill Card تولیدشده باشد، توسط نظارت به‌دلیل بدافزار مسدود نشده باشد و تأیید ClawScan پاک باشد.
- هویت Skill، هویت ناشر و فراداده نسخه انتخاب‌شده، فیلدهای سطح بالای پاکت هستند (`slug`، `displayName`، `publisherHandle`، `version`، `resolvedFrom`، `tag`، `createdAt`) تا خودکارسازی پوسته بتواند آن‌ها را بدون بازکردن پوشش‌های تودرتو بخواند.
- `security` حکم سطح بالای ClawScan/امنیت است. خودکارسازی باید بر اساس `ok`، `decision`، `reasons` و `security.status` عمل کند.
- `security.signals` شامل شواهد پشتیبان اسکنر مانند `staticScan`، `virusTotal` و `skillSpector` است.
- `security.signals.dependencyRegistry` برای سازگاری پاسخ v1 حفظ شده است، اما اسکنر وجود رجیستری وابستگی بازنشسته شده و این کلید همیشه `null` است.
- `provenance` فقط زمانی `server-resolved-github-import` است که ClawHub هنگام انتشار یا واردکردن، مخزن/ref/commit/path مربوط به GitHub را برطرف و ذخیره کرده باشد؛ در غیر این صورت `unavailable` است.

### `POST /api/v1/skills/-/security-verdicts`

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

درخواست:

```json
{
  "items": [{ "slug": "gifgrep", "version": "1.2.3" }]
}
```

نکته‌ها:

- `items` باید شامل 1-100 جفت یکتای `{ slug, version }` باشد.
- نتایج به‌ازای هر مورد هستند؛ نبودن یک Skill یا نسخه باعث شکست کل پاسخ نمی‌شود.
- پاسخ فقط امنیتی است. این پاسخ شامل داده‌های Skill Card، وضعیت کارت تولیدشده، فهرست فایل‌های آرتیفکت یا محموله‌های تفصیلی اسکنر نیست.
- `security.signals` فقط شامل شواهد پشتیبان در سطح وضعیت است؛ برای جزئیات کامل اسکنر از `/scan` یا صفحه ممیزی امنیتی ClawHub استفاده کنید.
- `security.signals.dependencyRegistry` برای سازگاری پاسخ v1 حفظ شده است، اما اسکنر وجود رجیستری وابستگی بازنشسته شده و این کلید همیشه `null` است.
- نبودن Skill Card بر `ok`، `decision` یا `reasons` این نقطه پایانی تأثیری ندارد؛ کلاینت‌ها هنگامی که به محتوای کارت نیاز دارند باید `skill-card.md` نصب‌شده را به‌صورت محلی بخوانند.
- هنگامی که به پاکت تأیید Skill Card تک‌مهارته نیاز دارید از `/verify`، هنگامی که به Markdown کارت تولیدشده نیاز دارید از `/card`، و هنگامی که به داده‌های تفصیلی اسکنر نیاز دارید از `/scan` استفاده کنید.

پاسخ:

```json
{
  "schema": "clawhub.skill.security-verdicts.v1",
  "items": [
    {
      "ok": true,
      "decision": "pass",
      "reasons": [],
      "requestedSlug": "gifgrep",
      "slug": "gifgrep",
      "displayName": "GifGrep",
      "publisherHandle": "steipete",
      "publisherDisplayName": "Peter",
      "requestedVersion": "1.2.3",
      "version": "1.2.3",
      "createdAt": 0,
      "checkedAt": 0,
      "skillUrl": "https://clawhub.ai/steipete/skills/gifgrep",
      "securityAuditUrl": "https://clawhub.ai/steipete/skills/gifgrep/security-audit?version=1.2.3",
      "security": {
        "status": "clean",
        "passed": true,
        "signals": {
          "staticScan": { "status": "clean", "reasonCodes": [] },
          "virusTotal": null,
          "skillSpector": null,
          "dependencyRegistry": null
        }
      }
    },
    {
      "ok": false,
      "decision": "fail",
      "reasons": ["version.not_found"],
      "requestedSlug": "missing-version",
      "requestedVersion": "1.0.0",
      "error": { "code": "version_not_found", "message": "نسخه یافت نشد" },
      "security": null
    }
  ]
}
```

### `GET /api/v1/skills/{slug}/file`

بایت‌های دقیق فایل ذخیره‌شده را به‌صورت دانلود برمی‌گرداند. برای درخواست پیش‌نمایش متن گریزشده و محدود،
`preview=1` را اضافه کنید؛ هر فایلی با بایت‌های معتبر UTF-8، صرف‌نظر از پسوند یا فراداده MIME آن،
قابل پیش‌نمایش است.

پارامترهای پرس‌وجو:

- `path` (الزامی)
- `version` (اختیاری)
- `tag` (اختیاری)
- `preview=1` (اختیاری؛ هنگامی که بایت‌ها UTF-8 معتبر نباشند، `text/plain` یا `415` را برمی‌گرداند)

نکته‌ها:

- مقدار پیش‌فرض، آخرین نسخه است.
- محدودیت دانلود خام: 10MB.
- محدودیت پیش‌نمایش متن: 200KB.

### `GET /api/v1/packages`

نقطه پایانی کاتالوگ یکپارچه برای:

- Skills
- Pluginهای کد
- Pluginهای بسته‌ای

پارامترهای پرس‌وجو:

- `limit` (اختیاری): عدد صحیح (1–100)
- `cursor` (اختیاری): مکان‌نمای صفحه‌بندی
- `family` (اختیاری): `skill`، `code-plugin` یا `bundle-plugin`
- `channel` (اختیاری): `official`، `community` یا `private`
- `isOfficial` (اختیاری): `true` یا `false`
- `sort` (اختیاری): `updated` (پیش‌فرض)، `recommended`، `trending`، `downloads`، نام مستعار قدیمی `installs`
- `category` (اختیاری): فیلتر دسته‌بندی Plugin. فقط زمانی پشتیبانی می‌شود که
  درخواست به بسته‌های Plugin محدود شده باشد (`/api/v1/plugins`،
  `/api/v1/code-plugins`، `/api/v1/bundle-plugins` یا نقاط پایانی بسته با
  `family=code-plugin`/`family=bundle-plugin`). دسته‌بندی‌های کنترل‌شده و
  نام‌های مستعار فیلتر قدیمی v1 در بخش `GET /api/v1/plugins` مستند شده‌اند.

نکته‌ها:

- مقادیر نامعتبر برای `family`، `channel`، `isOfficial`، `featured`،
  `highlightedOnly` یا `sort`، `400` را برمی‌گردانند. پارامترهای پرس‌وجوی ناشناخته نادیده گرفته می‌شوند.
- `GET /api/v1/code-plugins` و `GET /api/v1/bundle-plugins` به‌عنوان نام‌های مستعار خانواده ثابت باقی می‌مانند.
- ورودی‌های Skill همچنان بر رجیستری Skill متکی هستند و فقط از طریق `POST /api/v1/skills` قابل انتشارند.
- `POST /api/v1/packages` همچنان فقط برای انتشار Pluginهای کد و Pluginهای بسته‌ای است.
- فراخوان‌های ناشناس فقط کانال‌های عمومی بسته را می‌بینند.
- فراخوان‌های احراز هویت‌شده می‌توانند در نتایج فهرست/جست‌وجو، بسته‌های خصوصی ناشرانی را که به آن‌ها تعلق دارند ببینند.
- `channel=private` فقط بسته‌هایی را برمی‌گرداند که فراخوان احراز هویت‌شده بتواند آن‌ها را بخواند.

### `GET /api/v1/packages/search`

جست‌وجوی یکپارچه کاتالوگ در Skills + بسته‌های Plugin.

پارامترهای پرس‌وجو:

- `q` (الزامی): رشتهٔ پرس‌وجو
- `limit` (اختیاری): عدد صحیح (1–100)
- `family` (اختیاری): `skill`، `code-plugin` یا `bundle-plugin`
- `channel` (اختیاری): `official`، `community` یا `private`
- `isOfficial` (اختیاری): `true` یا `false`
- `category` (اختیاری): فیلتر دسته‌بندی Plugin. فقط زمانی پشتیبانی می‌شود که
  درخواست به بسته‌های Plugin محدود شده باشد. دسته‌بندی‌های کنترل‌شده و نام‌های مستعار
  فیلتر قدیمی v1 در بخش `GET /api/v1/plugins` مستند شده‌اند.

نکات:

- مقادیر نامعتبر برای `family`، `channel`، `isOfficial`، `featured` یا
  `highlightedOnly`، مقدار `400` را برمی‌گردانند. پارامترهای پرس‌وجوی ناشناخته نادیده گرفته می‌شوند.
- فراخوان‌های ناشناس فقط کانال‌های عمومی بسته را می‌بینند.
- فراخوان‌های احراز هویت‌شده می‌توانند بسته‌های خصوصی ناشرانی را که به آن‌ها تعلق دارند جست‌وجو کنند.
- `channel=private` فقط بسته‌هایی را برمی‌گرداند که فراخوان احراز هویت‌شده مجاز به خواندنشان باشد.

### `GET /api/v1/plugins`

مرور کاتالوگ مختص Plugin در میان بسته‌های code-plugin و bundle-plugin.

پارامترهای پرس‌وجو:

- `limit` (اختیاری): عدد صحیح (1-100)
- `cursor` (اختیاری): نشانگر صفحه‌بندی
- `isOfficial` (اختیاری): `true` یا `false`
- `sort` (اختیاری): `recommended` (پیش‌فرض)، `trending`، `downloads`، `updated`، نام مستعار قدیمی `installs`
- `category` (اختیاری): فیلتر دسته‌بندی Plugin. مقادیر فعلی:
  `channels`، `models`، `memory`، `context`، `voice`، `media`، `web`،
  `tools`، `runtime`، `gateway`، `security`، `other`.

نام‌های مستعار فیلتر قدیمی v1 همچنان در نقاط پایانی خواندن پذیرفته می‌شوند:

- `mcp-tooling`، `data` و `automation` به `tools` نگاشت می‌شوند.
- `observability` و `deployment` به `gateway` نگاشت می‌شوند.
- `dev-tools` به `runtime` نگاشت می‌شود.

`trending` جدول رتبه‌بندی نصب/دانلود هفت‌روزه است و از مجموع کل دوره استفاده نمی‌کند.
در نقطهٔ پایانی یکپارچهٔ `/api/v1/packages`، فقط مختص Plugin است؛ برای کاتالوگ Skills از
`/api/v1/skills?sort=trending` استفاده کنید.

نام‌های مستعار قدیمی به‌عنوان مقادیر دسته‌بندی ذخیره‌شده یا اعلام‌شده توسط نویسنده پذیرفته نمی‌شوند.

### `GET /api/v1/skills/export`

خروجی گروهی جدیدترین Skills عمومی برای تحلیل آفلاین.

احراز هویت:

- توکن API الزامی است.

پارامترهای پرس‌وجو:

- `startDate` (الزامی): کران پایین برحسب میلی‌ثانیهٔ یونیکس برای `updatedAt` مهارت.
- `endDate` (الزامی): کران بالا برحسب میلی‌ثانیهٔ یونیکس برای `updatedAt` مهارت.
- `limit` (اختیاری): عدد صحیح (1-250)، پیش‌فرض `250`.
- `cursor` (اختیاری): نشانگر صفحه‌بندی از پاسخ قبلی.

پاسخ:

- بدنه: بایگانی ZIP.
- ریشهٔ هر مهارت خروجی‌گرفته‌شده در `{publisher}/{slug}/` قرار دارد.
- Skills میزبانی‌شده شامل فایل‌های جدیدترین نسخهٔ ذخیره‌شده هستند و در
  `_manifest.json` با `sourceRef: "public-clawhub"` فهرست می‌شوند.
- Skills فعلی مبتنی بر GitHub که اسکن `clean` یا `suspicious` دارند، شامل
  `_source_handoff.json` با `sourceRef: "public-github"`، مخزن، کامیت، مسیر،
  هش محتوا و URL بایگانی هستند. آن‌ها فایل‌های منبع میزبانی‌شده در ClawHub را شامل نمی‌شوند.
- هر مهارت شامل `_export_skill_meta.json` است.
- `_manifest.json` همیشه در ریشهٔ ZIP قرار می‌گیرد.
- `_errors.json` زمانی گنجانده می‌شود که خروجی‌گرفتن از Skills یا فایل‌های منفرد
  ممکن نباشد.

سرآیندها:

- `X-Next-Cursor`
- `X-Has-More`
- `X-Total-Returned`
- `X-Date-Range`
- `X-Export-Errors`

### `GET /api/v1/plugins/export`

خروجی گروهی جدیدترین انتشارهای عمومی Plugin برای تحلیل آفلاین.

احراز هویت:

- توکن API الزامی است.

پارامترهای پرس‌وجو:

- `startDate` (الزامی): کران پایین برحسب میلی‌ثانیهٔ یونیکس برای `updatedAt` Plugin.
- `endDate` (الزامی): کران بالا برحسب میلی‌ثانیهٔ یونیکس برای `updatedAt` Plugin.
- `limit` (اختیاری): عدد صحیح (1-250)، پیش‌فرض `250`.
- `cursor` (اختیاری): نشانگر صفحه‌بندی از پاسخ قبلی.
- `family` (اختیاری): `code-plugin` یا `bundle-plugin`. حذف آن به‌معنای هر دو
  خانوادهٔ Plugin است.

پاسخ:

- بدنه: بایگانی ZIP.
- ریشهٔ هر Plugin خروجی‌گرفته‌شده در `{family}/{packageName}/` قرار دارد.
- هر Plugin خروجی‌گرفته‌شده شامل فایل‌های ذخیره‌شدهٔ جدیدترین انتشار است.
- فرادادهٔ خروجی هر Plugin در
  `__clawhub_export/{family}/{packageName}/plugin_meta.json` ذخیره می‌شود.
- `_manifest.json` همیشه در ریشهٔ ZIP قرار می‌گیرد.
- `_errors.json` زمانی گنجانده می‌شود که خروجی‌گرفتن از Pluginها یا فایل‌های منفرد
  ممکن نباشد.

سرآیندها:

- `X-Next-Cursor`
- `X-Has-More`
- `X-Total-Returned`
- `X-Date-Range`
- `X-Export-Errors`

### `GET /api/v1/plugins/search`

جست‌وجوی مختص Plugin در میان بسته‌های code-plugin و bundle-plugin.

پارامترهای پرس‌وجو:

- `q` (الزامی): رشتهٔ پرس‌وجو
- `limit` (اختیاری): عدد صحیح (1-100)
- `isOfficial` (اختیاری): `true` یا `false`
- `category` (اختیاری): فیلتر دسته‌بندی Plugin. مقادیر فعلی:
  `channels`، `models`، `memory`، `context`، `voice`، `media`، `web`،
  `tools`، `runtime`، `gateway`، `security`، `other`.

نکات:

- نام‌های مستعار فیلتر قدیمی v1 که در بخش `GET /api/v1/plugins` مستند شده‌اند نیز
  پذیرفته می‌شوند.
- فیلتر دسته‌بندی یک فیلتر واقعی API است که ردیف‌های چکیدهٔ دسته‌بندی Plugin از آن پشتیبانی می‌کنند،
  نه بازنویسی پرس‌وجوی جست‌وجو.
- نتایج به‌ترتیب ارتباط برگردانده می‌شوند و در حال حاضر صفحه‌بندی نمی‌شوند.
- کنترل‌های مرتب‌سازی رابط کاربری مرورگر برای جست‌وجوی Plugin، نتایج بارگذاری‌شده را بر اساس ارتباط
  و مطابق با رفتار فعلی مرور `/skills` بازمرتب می‌کنند.

### `GET /api/v1/packages/{name}`

فرادادهٔ جزئیات بسته را برمی‌گرداند.

نکات:

- Skills نیز می‌توانند در کاتالوگ یکپارچه از طریق این مسیر تفکیک شوند.
- بسته‌های خصوصی مقدار `404` را برمی‌گردانند، مگر اینکه فراخوان مجاز به خواندن ناشر مالک باشد.

### `DELETE /api/v1/packages/{name}`

یک بسته و همهٔ انتشارهای آن را به‌صورت نرم حذف می‌کند.

نکات:

- به توکن API مالک بسته، مالک/مدیر ناشر سازمانی،
  ناظر پلتفرم یا مدیر پلتفرم نیاز دارد.

### `GET /api/v1/packages/{name}/versions`

تاریخچهٔ نسخه‌ها را برمی‌گرداند.

پارامترهای پرس‌وجو:

- `limit` (اختیاری): عدد صحیح (1–100)
- `cursor` (اختیاری): نشانگر صفحه‌بندی

نکات:

- بسته‌های خصوصی مقدار `404` را برمی‌گردانند، مگر اینکه فراخوان مجاز به خواندن ناشر مالک باشد.

### `GET /api/v1/packages/{name}/versions/{version}`

یک نسخهٔ بسته را شامل فرادادهٔ فایل، سازگاری،
اعتبارسنجی، فرادادهٔ آرتیفکت و داده‌های اسکن برمی‌گرداند.

نکات:

- `version.artifact.kind` برای بایگانی‌های بستهٔ قدیمی `legacy-zip` یا
  برای انتشارهای مبتنی بر ClawPack مقدار `npm-pack` است.
- انتشارهای ClawPack شامل فیلدهای سازگار با npm یعنی `npmIntegrity`، `npmShasum` و
  `npmTarballName` هستند.
- `version.sha256hash` فرادادهٔ سازگاری منسوخ‌شده برای کلاینت‌های قدیمی است. این فیلد
  دقیقاً بایت‌های ZIP برگردانده‌شده توسط `/api/v1/packages/{name}/download` را هش می‌کند.
  کلاینت‌های جدید باید از `version.artifact.sha256` استفاده کنند که
  آرتیفکت انتشار استاندارد را شناسایی می‌کند.
- `version.vtAnalysis`، `version.llmAnalysis` و `version.staticScan`
  در صورت وجود داده‌های اسکن گنجانده می‌شوند.
- بسته‌های خصوصی مقدار `404` را برمی‌گردانند، مگر اینکه فراخوان مجاز به خواندن ناشر مالک باشد.

### `GET /api/v1/packages/{name}/versions/{version}/security`

خلاصهٔ دقیق امنیت و اعتماد انتشار بسته را برای کلاینت‌های نصب
برمی‌گرداند. این سطح مصرف عمومی OpenClaw برای تصمیم‌گیری دربارهٔ امکان نصب
یک انتشار تفکیک‌شده است.

احراز هویت:

- نقطهٔ پایانی خواندن عمومی. هیچ توکن مالک، ناشر، ناظر یا مدیری
  لازم نیست.

پاسخ:

```json
{
  "package": {
    "name": "@openclaw/example-plugin",
    "displayName": "Example Plugin",
    "family": "code-plugin"
  },
  "release": {
    "releaseId": "packageReleases:...",
    "version": "1.2.3",
    "artifactKind": "npm-pack",
    "artifactSha256": "0123456789abcdef...",
    "npmIntegrity": "sha512-...",
    "npmShasum": "0123456789abcdef0123456789abcdef01234567",
    "npmTarballName": "example-plugin-1.2.3.tgz",
    "createdAt": 1730000000000
  },
  "trust": {
    "scanStatus": "malicious",
    "moderationState": "quarantined",
    "blockedFromDownload": true,
    "reasons": ["manual:quarantined", "scan:malicious"],
    "pending": false,
    "stale": false
  }
}
```

فیلدهای پاسخ:

- `package.name`، `package.displayName` و `package.family`
  بستهٔ تفکیک‌شدهٔ رجیستری را شناسایی می‌کنند.
- `release.releaseId`، `release.version` و `release.createdAt`
  انتشار دقیقی را که ارزیابی شده است شناسایی می‌کنند.
- `release.artifactKind`، `release.artifactSha256`، `release.npmIntegrity`،
  `release.npmShasum` و `release.npmTarballName` در صورت شناخته‌شدن برای
  آرتیفکت انتشار وجود دارند.
- `trust.scanStatus` وضعیت اعتماد مؤثری است که از ورودی‌های اسکنر
  و نظارت دستی انتشار به‌دست می‌آید.
- `trust.moderationState` می‌تواند تهی باشد. وقتی هیچ نظارت دستی بر انتشار
  وجود ندارد، مقدار آن `null` است.
- `trust.blockedFromDownload` سیگنال مسدودسازی نصب است. وقتی این مقدار `true` است، OpenClaw و سایر
  کلاینت‌های نصب باید به‌جای استخراج دوبارهٔ قواعد مسدودسازی از فیلدهای اسکنر یا نظارت،
  نصب را مسدود کنند.
- `trust.reasons` فهرست توضیحات قابل‌نمایش به کاربر و ممیزی است. کدهای دلیل
  رشته‌های پایدار و فشرده‌ای مانند `manual:quarantined`، `scan:malicious`
  و `package:malicious` هستند.
- `trust.pending` یعنی یک یا چند ورودی اعتماد همچنان در انتظار تکمیل هستند.
- `trust.stale` یعنی خلاصهٔ اعتماد از ورودی‌های قدیمی محاسبه شده است و
  پیش از تصمیم اجازه‌دادن با اطمینان بالا، باید نیازمند نوسازی تلقی شود.

نکات:

- این نقطهٔ پایانی دقیقاً وابسته به نسخه است. کلاینت‌ها باید آن را پس از تفکیک
  نسخهٔ بسته‌ای که قصد نصبش را دارند فراخوانی کنند، نه صرفاً پس از خواندن جدیدترین
  فرادادهٔ بسته.
- بسته‌های خصوصی مقدار `404` را برمی‌گردانند، مگر اینکه فراخوان مجاز به خواندن ناشر مالک باشد.
- این نقطهٔ پایانی عمداً محدودتر از نقاط پایانی نظارت مالک/ناظر
  است. تصمیم نصب و توضیح عمومی را آشکار می‌کند، نه
  هویت گزارش‌دهندگان، متن گزارش‌ها، شواهد خصوصی یا جدول‌های زمانی بازبینی
  داخلی را.

### `GET /api/v1/packages/{name}/versions/{version}/artifact`

فرادادهٔ صریح تفکیک‌کنندهٔ آرتیفکت را برای یک نسخهٔ بسته برمی‌گرداند.

نکات:

- نسخه‌های قدیمی بسته یک آرتیفکت `legacy-zip` و یک
  `downloadUrl` قدیمی ZIP را برمی‌گردانند.
- نسخه‌های ClawPack یک آرتیفکت `npm-pack`، فیلدهای یکپارچگی npm، یک
  `tarballUrl` و URL سازگاری قدیمی ZIP را برمی‌گردانند.
- این سطح تفکیک‌کنندهٔ OpenClaw است؛ از حدس‌زدن قالب بایگانی بر اساس
  یک URL مشترک جلوگیری می‌کند.

### `GET /api/v1/packages/{name}/versions/{version}/artifact/download`

آرتیفکت نسخه را از طریق مسیر صریح تفکیک‌کننده دانلود می‌کند.

نکات:

- نسخه‌های ClawPack دقیقاً بایت‌های `.tgz` مربوط به npm-pack بارگذاری‌شده را استریم می‌کنند.
- نسخه‌های ZIP قدیمی به `/api/v1/packages/{name}/download?version=` هدایت می‌شوند.
- از سطل نرخ دانلود استفاده می‌کند.

### `GET /api/v1/packages/{name}/readiness`

آمادگی محاسبه‌شده برای استفاده آتی OpenClaw را برمی‌گرداند.

بررسی‌های آمادگی شامل موارد زیر است:

- وضعیت کانال رسمی
- دردسترس‌بودن آخرین نسخه
- دردسترس‌بودن آرتیفکت npm-pack متعلق به ClawPack
- چکیده آرتیفکت
- منشأ مخزن منبع و کامیت
- فراداده سازگاری OpenClaw
- مقصدهای میزبان
- وضعیت اسکن

پاسخ:

```json
{
  "package": {
    "name": "@openclaw/example-plugin",
    "displayName": "Plugin نمونه",
    "family": "code-plugin",
    "isOfficial": true,
    "latestVersion": "1.2.3"
  },
  "ready": false,
  "checks": [
    {
      "id": "clawpack",
      "label": "آرتیفکت ClawPack",
      "status": "fail",
      "message": "آخرین نسخه قدیمی و فقط به‌صورت ZIP است."
    }
  ],
  "blockers": ["clawpack"]
}
```

### `GET /api/v1/packages/migrations`

نقطه پایانی مدیر ناظر برای فهرست‌کردن ردیف‌های مهاجرت Plugin رسمی OpenClaw.

احراز هویت:

- به توکن API برای کاربر مدیر ناظر یا مدیر سامانه نیاز دارد.

پارامترهای پرس‌وجو:

- `phase` (اختیاری): `planned`، `published`، `clawpack-ready`،
  `legacy-zip-only`، `metadata-ready`، `blocked`، `ready-for-openclaw`، یا
  `all` (پیش‌فرض).
- `limit` (اختیاری): عدد صحیح (1-100)
- `cursor` (اختیاری): مکان‌نمای صفحه‌بندی

پاسخ:

```json
{
  "items": [
    {
      "migrationId": "officialPluginMigrations:...",
      "bundledPluginId": "core.search",
      "packageName": "@openclaw/search-plugin",
      "packageId": "packages:...",
      "owner": "platform",
      "sourceRepo": "openclaw/openclaw",
      "sourcePath": "plugins/search",
      "sourceCommit": "abc123",
      "phase": "blocked",
      "blockers": ["ClawPack موجود نیست"],
      "hostTargetsComplete": true,
      "scanClean": false,
      "moderationApproved": false,
      "runtimeBundlesReady": false,
      "notes": null,
      "createdAt": 1760000000000,
      "updatedAt": 1760000000000
    }
  ],
  "nextCursor": null,
  "done": true
}
```

### `POST /api/v1/packages/migrations`

نقطه پایانی مدیر سامانه برای ایجاد یا به‌روزرسانی ردیف مهاجرت یک Plugin رسمی.

احراز هویت:

- به توکن API برای کاربر مدیر سامانه نیاز دارد.

بدنه درخواست:

```json
{
  "bundledPluginId": "core.search",
  "packageName": "@openclaw/search-plugin",
  "owner": "platform",
  "sourceRepo": "openclaw/openclaw",
  "sourcePath": "plugins/search",
  "sourceCommit": "abc123",
  "phase": "blocked",
  "blockers": ["ClawPack موجود نیست"],
  "hostTargetsComplete": true,
  "scanClean": false,
  "moderationApproved": false,
  "runtimeBundlesReady": false,
  "notes": "در انتظار بارگذاری ناشر"
}
```

نکات:

- `bundledPluginId` به حروف کوچک نرمال‌سازی می‌شود و کلید پایدار درج یا به‌روزرسانی است.
- `packageName` مطابق نام npm نرمال‌سازی می‌شود؛ بسته ممکن است برای مهاجرت‌های
  برنامه‌ریزی‌شده موجود نباشد.
- این فقط آمادگی مهاجرت را پیگیری می‌کند. OpenClaw را تغییر نمی‌دهد و
  ClawPack تولید نمی‌کند.

### `GET /api/v1/packages/moderation/queue`

نقطه پایانی مدیر ناظر/مدیر سامانه برای صف‌های بازبینی انتشار بسته.

احراز هویت:

- به توکن API برای کاربر مدیر ناظر یا مدیر سامانه نیاز دارد.

پارامترهای پرس‌وجو:

- `status` (اختیاری): `open` (پیش‌فرض)، `blocked`، `manual`، یا `all`
- `limit` (اختیاری): عدد صحیح (1-100)
- `cursor` (اختیاری): مکان‌نمای صفحه‌بندی

معانی وضعیت‌ها:

- `open`: انتشارهای مشکوک، مخرب، در انتظار، قرنطینه‌شده، لغوشده یا گزارش‌شده.
- `blocked`: انتشارهای قرنطینه‌شده، لغوشده یا مخرب.
- `manual`: هر انتشاری که بازنویسی دستی نظارت دارد.
- `all`: هر انتشاری که بازنویسی دستی، وضعیت اسکن غیرپاک یا گزارش بسته دارد.

پاسخ:

```json
{
  "items": [
    {
      "packageId": "packages:...",
      "releaseId": "packageReleases:...",
      "name": "@openclaw/example-plugin",
      "displayName": "Plugin نمونه",
      "family": "code-plugin",
      "channel": "community",
      "isOfficial": false,
      "version": "1.2.3",
      "createdAt": 1730000000000,
      "artifactKind": "npm-pack",
      "scanStatus": "malicious",
      "moderationState": "quarantined",
      "moderationReason": "بازبینی دستی",
      "sourceRepo": "openclaw/example-plugin",
      "sourceCommit": "abc123",
      "reportCount": 2,
      "lastReportedAt": 1730000001000,
      "reasons": ["manual:quarantined", "scan:malicious", "reports:2"]
    }
  ],
  "nextCursor": null,
  "done": true
}
```

### `POST /api/v1/packages/{name}/report`

یک بسته را برای بازبینی مدیر ناظر گزارش می‌کند. گزارش‌ها در سطح بسته هستند و به‌صورت اختیاری
به یک نسخه پیوند می‌خورند. آن‌ها صف نظارت را تغذیه می‌کنند، اما به‌تنهایی بسته را به‌طور خودکار پنهان نمی‌کنند یا
دانلودها را مسدود نمی‌سازند؛ مدیران ناظر باید از نظارت انتشار برای
تأیید، قرنطینه یا لغو آرتیفکت‌ها استفاده کنند.

احراز هویت:

- به توکن API نیاز دارد.

درخواست:

```json
{ "reason": "فایل دودویی بومی مشکوک", "version": "1.2.3" }
```

پاسخ:

```json
{
  "ok": true,
  "reported": true,
  "alreadyReported": false,
  "packageId": "packages:...",
  "releaseId": "packageReleases:...",
  "reportCount": 1
}
```

### `GET /api/v1/packages/reports`

نقطه پایانی مدیر ناظر/مدیر سامانه برای دریافت گزارش‌های بسته.

احراز هویت:

- به توکن API برای کاربر مدیر ناظر یا مدیر سامانه نیاز دارد.

پارامترهای پرس‌وجو:

- `status` (اختیاری): `open` (پیش‌فرض)، `confirmed`، `dismissed`، یا `all`
- `limit` (اختیاری): عدد صحیح (1-100)
- `cursor` (اختیاری): مکان‌نمای صفحه‌بندی

پاسخ:

```json
{
  "items": [
    {
      "reportId": "packageReports:...",
      "packageId": "packages:...",
      "releaseId": "packageReleases:...",
      "name": "@openclaw/example-plugin",
      "displayName": "Plugin نمونه",
      "family": "code-plugin",
      "version": "1.2.3",
      "reason": "فایل دودویی بومی مشکوک",
      "status": "open",
      "createdAt": 1730000000000,
      "reporter": {
        "userId": "users:...",
        "handle": "reporter",
        "displayName": "گزارش‌دهنده"
      },
      "triagedAt": null,
      "triagedBy": null,
      "triageNote": null
    }
  ],
  "nextCursor": null,
  "done": true
}
```

### `GET /api/v1/packages/{name}/moderation`

نقطه پایانی مالک/مدیر ناظر برای مشاهده وضعیت نظارت بسته.

احراز هویت:

- به توکن API برای مالک بسته، عضو ناشر، مدیر ناظر یا
  کاربر مدیر سامانه نیاز دارد.

پاسخ:

```json
{
  "package": {
    "packageId": "packages:...",
    "name": "@openclaw/example-plugin",
    "displayName": "Plugin نمونه",
    "family": "code-plugin",
    "channel": "community",
    "isOfficial": false,
    "reportCount": 2,
    "lastReportedAt": 1730000001000,
    "scanStatus": "malicious"
  },
  "latestRelease": {
    "releaseId": "packageReleases:...",
    "version": "1.2.3",
    "artifactKind": "npm-pack",
    "scanStatus": "malicious",
    "moderationState": "quarantined",
    "moderationReason": "بازبینی دستی",
    "blockedFromDownload": true,
    "reasons": ["manual:quarantined", "scan:malicious", "reports:2"],
    "createdAt": 1730000000000
  }
}
```

### `POST /api/v1/packages/reports/{reportId}/triage`

نقطه پایانی مدیر ناظر/مدیر سامانه برای حل‌وفصل یا بازگشایی گزارش‌های بسته.

درخواست:

```json
{
  "status": "confirmed",
  "note": "انتشار متأثر بازبینی و قرنطینه شد.",
  "finalAction": "quarantine"
}
```

`note` برای `confirmed` و `dismissed` الزامی است؛ هنگام
بازگرداندن `status` به `open` می‌توان آن را حذف کرد. برای اعمال نظارت انتشار در
همان گردش‌کار قابل‌ممیزی، `finalAction: "quarantine"` یا
`finalAction: "revoke"` را همراه گزارش تأییدشده ارسال کنید.

پاسخ:

```json
{
  "ok": true,
  "reportId": "packageReports:...",
  "packageId": "packages:...",
  "status": "confirmed",
  "reportCount": 0
}
```

### `POST /api/v1/packages/{name}/versions/{version}/moderation`

نقطه پایانی مدیر ناظر/مدیر سامانه برای بازبینی انتشار بسته.

درخواست:

```json
{ "state": "quarantined", "reason": "محموله بومی مشکوک." }
```

وضعیت‌های پشتیبانی‌شده:

- `approved`: به‌صورت دستی بازبینی و مجاز شده است.
- `quarantined`: تا زمان پیگیری مسدود شده است.
- `revoked`: پس از آنکه انتشار قبلاً مورد اعتماد بوده، مسدود شده است.

مسیرهای دانلود آرتیفکت برای انتشارهای قرنطینه‌شده و لغوشده، `403` را برمی‌گردانند.
هر تغییر یک ورودی در گزارش ممیزی ثبت می‌کند.

### `GET /api/v1/packages/{name}/file`

بایت‌های دقیق فایل ذخیره‌شده بسته را به‌صورت دانلود برمی‌گرداند. برای درخواست همان پیش‌نمایش متنی محدودشده
UTF-8 که برای فایل‌های skill استفاده می‌شود، `preview=1` را اضافه کنید.

پارامترهای پرس‌وجو:

- `path` (الزامی)
- `version` (اختیاری)
- `tag` (اختیاری)
- `preview=1` (اختیاری؛ وقتی بایت‌ها UTF-8 معتبر نباشند، `text/plain` یا `415` را برمی‌گرداند)

نکات:

- پیش‌فرض، آخرین انتشار است.
- از سطل نرخ خواندن استفاده می‌کند، نه سطل دانلود.
- محدودیت دانلود خام: 10MB.
- محدودیت پیش‌نمایش متن: 200KB؛ فایل‌های مات فقط برای درخواست‌های پیش‌نمایش، `415` را برمی‌گردانند.
- اسکن‌های در انتظار VirusTotal خواندن را مسدود نمی‌کنند؛ انتشارهای مخرب ممکن است همچنان در جای دیگری ارائه نشوند.
- بسته‌های خصوصی `404` را برمی‌گردانند، مگر اینکه فراخواننده بتواند ناشر مالک را بخواند.

### `GET /api/v1/packages/{name}/download`

آرشیو ZIP قطعی قدیمی را برای انتشار یک بسته دانلود می‌کند.

پارامترهای پرس‌وجو:

- `version` (اختیاری)
- `tag` (اختیاری)

نکات:

- پیش‌فرض، آخرین انتشار است.
- Skills به `GET /api/v1/download` هدایت می‌شوند.
- آرشیوهای Plugin/بسته، فایل‌های zip با ریشه `package/` هستند تا کلاینت‌های قدیمی OpenClaw
  همچنان کار کنند.
- این مسیر فقط ZIP باقی می‌ماند. فایل‌های `.tgz` متعلق به ClawPack را استریم نمی‌کند.
- پاسخ‌ها برای بررسی یکپارچگی حل‌کننده شامل سرآیندهای `ETag`، `Digest`، `X-ClawHub-Artifact-Type` و
  `X-ClawHub-Artifact-Sha256` هستند.
- فراداده مختص رجیستری به آرشیو دانلودشده تزریق نمی‌شود.
- اسکن‌های در انتظار VirusTotal دانلودها را مسدود نمی‌کنند؛ انتشارهای مخرب `403` را برمی‌گردانند.
- بسته‌های خصوصی `404` را برمی‌گردانند، مگر اینکه فراخواننده مالک باشد.

### `GET /api/npm/{package}`

یک packument سازگار با npm برای نسخه‌های بسته مبتنی بر ClawPack برمی‌گرداند.

نکات:

- فقط نسخه‌هایی فهرست می‌شوند که tarballهای npm-pack مربوط به ClawPack برای آن‌ها بارگذاری شده باشد.
- نسخه‌های قدیمی که فقط ZIP هستند عمداً حذف می‌شوند.
- `dist.tarball`، `dist.integrity` و `dist.shasum` از فیلدهای سازگار با npm
  استفاده می‌کنند تا کاربران در صورت تمایل npm را به آینه هدایت کنند.
- packumentهای بسته‌های scoped هم از `/api/npm/@scope/name` و هم از مسیر درخواست کدگذاری‌شده npm یعنی
  `/api/npm/@scope%2Fname` پشتیبانی می‌کنند.

### `GET /api/npm/{package}/-/{tarball}.tgz`

بایت‌های دقیق tarball بارگذاری‌شده ClawPack را برای کلاینت‌های آینه npm استریم می‌کند.

نکات:

- از سطل نرخ دانلود استفاده می‌کند.
- سرآیندهای دانلود شامل SHA-256 متعلق به ClawHub به‌همراه فراداده integrity/shasum مربوط به npm هستند.
- بررسی‌های نظارت و دسترسی به بسته خصوصی همچنان اعمال می‌شوند.

### `GET /api/v1/resolve`

CLI از این برای نگاشت اثر انگشت محلی به یک نسخه شناخته‌شده استفاده می‌کند.

پارامترهای پرس‌وجو:

- `slug` (الزامی)
- `hash` (الزامی): sha256 هگزادسیمال 64 نویسه‌ای اثر انگشت بسته

پاسخ:

```json
{ "slug": "gifgrep", "match": { "version": "1.2.2" }, "latestVersion": { "version": "1.2.3" } }
```

### `GET /api/v1/download`

نسخهٔ میزبانی‌شدهٔ یک Skill را به‌صورت ZIP بارگیری می‌کند، یا برای یک Skill فعلی مبتنی بر GitHub که دارای اسکن `clean` یا `suspicious` است و نسخهٔ میزبانی‌شده ندارد، واگذاری به منبع GitHub را برمی‌گرداند.

پارامترهای کوئری:

- `slug` (الزامی)
- `version` (اختیاری): رشتهٔ semver
- `tag` (اختیاری): نام تگ (برای مثال `latest`)

نکات:

- اگر نه `version` و نه `tag` ارائه شده باشد، از آخرین نسخه استفاده می‌شود.
- نسخه‌های حذف‌شده به‌صورت نرم، `410` را برمی‌گردانند.
- واگذاری‌های Skill مبتنی بر GitHub، بایت‌ها را پروکسی یا آینه نمی‌کنند. پاسخ JSON
  شامل `sourceRef: "public-github"`، `repo`، `commit`، `path`، `contentHash`
  و `archiveUrl` است؛ وضعیت اسکن/فعلی یک شرط عبور است و در فرادادهٔ محمولهٔ
  موفقیت گنجانده نمی‌شود.
- آمار بارگیری به‌صورت هویت‌های یکتا در هر روز UTC محاسبه می‌شود (وقتی توکن API معتبر است `userId`، در غیر این صورت IP).

## نقاط پایانی احراز هویت (توکن Bearer)

همهٔ نقاط پایانی نیازمند موارد زیر هستند:

```
Authorization: Bearer clh_...
```

### `GET /api/v1/whoami`

توکن را اعتبارسنجی می‌کند و شناسهٔ کاربر را برمی‌گرداند.

### `POST /api/v1/skills`

نسخه‌ای جدید منتشر می‌کند.

- روش ترجیحی: `multipart/form-data` با JSON از نوع `payload` به‌همراه blobهای `files[]`.
- بدنهٔ JSON دارای `files` (مبتنی بر storageId) نیز پذیرفته می‌شود.
- فیلد اختیاری محموله: `ownerHandle`. در صورت وجود، API آن
  ناشر را در سمت سرور پیدا می‌کند و عامل باید دسترسی ناشر داشته باشد.
- فیلد اختیاری محموله: `migrateOwner`. وقتی `true` همراه با `ownerHandle` باشد،
  یک Skill موجود می‌تواند به آن مالک منتقل شود، مشروط بر اینکه عامل در هر دو
  ناشر فعلی و مقصد، مدیر/مالک باشد. بدون این اعلام موافقت، تغییر مالک
  رد می‌شود.

### `POST /api/v1/packages`

یک انتشار code-plugin یا bundle-plugin را منتشر می‌کند.

- به احراز هویت با توکن Bearer نیاز دارد.
- به `multipart/form-data` نیاز دارد.
- فیلدهای مجاز فرم عبارت‌اند از `payload`، blobهای تکرارشوندهٔ `files`، یا یک ارجاع
  tarball از نوع `clawpack`. مقدار `clawpack` می‌تواند یک blob از نوع `.tgz` یا شناسهٔ ذخیره‌سازی برگردانده‌شده از
  جریان upload-url باشد. انتشارهای مرحله‌بندی‌شده با شناسهٔ ذخیره‌سازی باید
  `clawpackUploadTicket` برگردانده‌شده همراه آن URL بارگذاری را نیز شامل شوند.
- از یکی از `files` یا `clawpack` استفاده کنید؛ هرگز هر دو را در یک درخواست به‌کار نبرید.
- بدنه‌های JSON و فرادادهٔ `payload.files` / `payload.artifact`
  ارائه‌شده توسط فراخواننده رد می‌شوند.
- درخواست‌های انتشار مستقیم multipart به 18MB محدود هستند. tarballهای ClawPack می‌توانند
  تا سقف 120MB برای tarball از جریان upload-url استفاده کنند.
- فیلد اختیاری محموله: `ownerHandle`. در صورت وجود، فقط مدیران می‌توانند از طرف آن مالک منتشر کنند.

نکات مهم اعتبارسنجی:

- `family` باید `code-plugin` یا `bundle-plugin` باشد.
- بسته‌های Plugin به `openclaw.plugin.json` نیاز دارند. بارگذاری‌های `.tgz` در ClawPack باید
  آن را در `package/openclaw.plugin.json` داشته باشند.
- Pluginهای کد به `package.json`، فرادادهٔ مخزن منبع، فرادادهٔ commit منبع،
  فرادادهٔ شِمای پیکربندی، `openclaw.compat.pluginApi` و
  `openclaw.build.openclawVersion` نیاز دارند.
- `openclaw.hostTargets` و `openclaw.environment` فراداده‌های اختیاری هستند.
- فقط ناشر سازمانی `openclaw` و ناشران شخصی اعضای فعلی سازمان `openclaw`
  می‌توانند در کانال `official` منتشر کنند.
- انتشارهای نیابتی همچنان واجد شرایط بودن برای کانال رسمی را نسبت به حساب مالک مقصد اعتبارسنجی می‌کنند.

### `DELETE /api/v1/skills/{slug}` / `POST /api/v1/skills/{slug}/undelete`

حذف نرم / بازیابی یک Skill (مالک، ناظر یا مدیر).

بدنهٔ JSON اختیاری:

```json
{ "reason": "برای نظارت تا زمان بررسی حقوقی نگه داشته شده است." }
```

در صورت وجود، `reason` به‌عنوان یادداشت نظارت Skill ذخیره و در گزارش ممیزی کپی می‌شود.
حذف‌های نرم آغازشده توسط مالک، slug را برای 30 روز رزرو می‌کنند و پس از آن ناشر
دیگری می‌تواند slug را تصاحب کند. هنگامی که این انقضا اعمال شود، پاسخ حذف شامل `slugReservedUntil` است.
پنهان‌سازی‌های ناظر/مدیر و حذف‌های امنیتی به این شکل منقضی نمی‌شوند.

پاسخ حذف:

```json
{ "ok": true, "slugReservedUntil": 1730000000000 }
```

کدهای وضعیت:

- `200`: موفق
- `401`: احراز هویت نشده
- `403`: ممنوع
- `404`: Skill/کاربر یافت نشد
- `500`: خطای داخلی سرور

### `POST /api/v1/users/publisher`

فقط مدیر. اطمینان می‌دهد که برای یک شناسه، ناشر سازمانی وجود دارد. اگر شناسه همچنان به یک
ناشر اشتراکی قدیمی کاربر/شخصی اشاره کند، نقطهٔ پایانی ابتدا آن را به ناشر سازمانی مهاجرت می‌دهد.
برای سازمانی که به‌تازگی ایجاد شده است، `memberHandle` را ارائه کنید؛ مدیر عامل به‌عنوان عضو افزوده نمی‌شود.
مقدار پیش‌فرض `memberRole` برابر با `owner` است.

- بدنه: `{ "handle": "openclaw", "displayName": "OpenClaw", "memberHandle": "alice", "memberRole": "owner", "trusted": true }`
- پاسخ: `{ "ok": true, "publisherId": "...", "handle": "openclaw", "created": true, "migrated": false, "trusted": true, "member": { "userId": "...", "handle": "alice", "role": "owner" } }`

### `POST /api/v1/publishers`

ایجاد سلف‌سرویس و احراز هویت‌شدهٔ ناشر سازمانی. یک ناشر سازمانی جدید ایجاد می‌کند و
فراخواننده را به‌عنوان مالک می‌افزاید. این نقطهٔ پایانی شناسه‌های موجود کاربر/شخصی را مهاجرت نمی‌دهد و
ناشر را قابل‌اعتماد/رسمی علامت‌گذاری نمی‌کند.

- بدنه: `{ "handle": "opik", "displayName": "Opik" }`
- پاسخ: `{ "ok": true, "publisherId": "...", "handle": "opik", "created": true, "trusted": false }`
- وقتی شناسه از قبل توسط یک ناشر، کاربر یا ناشر شخصی استفاده شده باشد، `409` را برمی‌گرداند.

### `POST /api/v1/users/reserve`

فقط مدیر. بدون انتشار یک نسخه، slugهای ریشه و نام بسته‌ها را برای مالک برحق رزرو می‌کند.
نام بسته‌ها به بسته‌های جایگزین خصوصی بدون ردیف انتشار تبدیل می‌شوند تا همان
مالک بتواند بعداً انتشار واقعی code-plugin یا bundle-plugin را با آن نام منتشر کند.

- بدنه: `{ "handle": "openclaw", "slugs": ["diffs"], "packageNames": ["@openclaw/diffs"], "reason": "reserved for official OpenClaw plugin" }`
- پاسخ: `{ "ok": true, "succeeded": 2, "failed": 0, "results": [{ "kind": "slug", "name": "diffs", "ok": true, "action": "reserved" }] }`

### `POST /api/v1/users/publisher-recovery`

فقط مدیر. یک ناشر شخصی را برای اصل GitHub OAuth جایگزین و تأییدشده،
بدون ویرایش ردیف‌های حساب Convex Auth، بازیابی می‌کند. درخواست باید هر دو شناسهٔ تغییرناپذیر حساب
ارائه‌دهندهٔ GitHub را مشخص کند؛ شناسه‌های تغییرپذیر فقط به‌عنوان حفاظی برای اپراتور استفاده می‌شوند.

حالت پیش‌فرض نقطهٔ پایانی dry-run است. اعمال بازیابی به `dryRun: false` و
`confirmIdentityVerified: true` نیاز دارد، پس از آنکه کارکنان به‌طور مستقل تداوم میان هر دو
اصل GitHub را تأیید کنند. اگر ناشر شخصی فعلی کاربر مقصد دارای Skillها، بسته‌ها یا منابع Skill در GitHub باشد،
بازیابی به‌صورت fail-closed متوقف می‌شود.
بازیابی همچنین فیلدهای قدیمی `ownerUserId` را برای Skillهای ناشر بازیابی‌شده،
نام‌های مستعار slug در Skill، بسته‌ها، هشدارهای بازرس بسته و ردیف‌های مشتق‌شدهٔ چکیدهٔ جست‌وجو مهاجرت می‌دهد تا
مسیرهای مالک مستقیم با اختیار ناشر جدید هم‌خوان باشند. رزرو فعال شناسهٔ محافظت‌شده
برای شناسهٔ بازیابی‌شده نیز به کاربر جایگزین واگذار می‌شود تا همگام‌سازی بعدی
پروفایل نتواند اختیار رقیب کاربر قبلی را بازگرداند. هر جدول اصلی در هر تراکنش اعمال به
100 ردیف محدود است؛ بازیابی‌های بزرگ‌تر باید ابتدا از یک مهاجرت قابل‌ازسرگیری مالک استفاده کنند.
منابع Skill در GitHub در سطح ناشر هستند و به‌جای بازنویسی، به‌عنوان بررسی‌شده گزارش می‌شوند.

- بدنه: `{ "handle": "gingiris", "nextUserHandle": "gingiris-1031", "previousGitHubProviderAccountId": "123", "nextGitHubProviderAccountId": "456", "reason": "Verified account continuity for issue #2555", "confirmIdentityVerified": true, "dryRun": false }`
- پاسخ: `{ "ok": true, "dryRun": false, "recovered": true, "publisherId": "...", "handle": "gingiris", "previousUser": { "userId": "...", "handle": "gingiris", "nextHandle": "gingiris-recovered", "githubProviderAccountId": "123", "authAccountCount": 1 }, "nextUser": { "userId": "...", "handle": "gingiris-1031", "nextHandle": "gingiris", "githubProviderAccountId": "456", "authAccountCount": 1 }, "retiredPersonalPublisher": null, "resourceOwnerMigration": { "limitPerTable": 100, "skills": 1, "skillSlugAliases": 1, "packages": 0, "packageInspectorWarnings": 0, "githubSourcesChecked": 1, "handleReservations": 1 }, "identityVerified": true, "reason": "Verified account continuity for issue #2555" }`

### نقاط پایانی مدیریت slug مالک

- `POST /api/v1/skills/{slug}/rename`
  - بدنه: `{ "newSlug": "new-canonical-slug" }`
  - پاسخ: `{ "ok": true, "slug": "new-canonical-slug", "previousSlug": "old-slug" }`
- `POST /api/v1/skills/{slug}/merge`
  - بدنه: `{ "targetSlug": "canonical-target-slug" }`
  - پاسخ: `{ "ok": true, "sourceSlug": "old-slug", "targetSlug": "canonical-target-slug" }`

نکات:

- هر دو نقطهٔ پایانی به احراز هویت با توکن API نیاز دارند و فقط برای مالک Skill کار می‌کنند.
- `rename`، slug قبلی را به‌عنوان نام مستعار تغییرمسیر حفظ می‌کند.
- `merge`، فهرست منبع را پنهان می‌کند و slug منبع را به فهرست مقصد تغییرمسیر می‌دهد.

### نقاط پایانی انتقال مالکیت

- `POST /api/v1/skills/{slug}/transfer`
  - بدنه: `{ "toUserHandle": "target_handle", "message": "optional" }`
  - پاسخ: `{ "ok": true, "transferId": "skillOwnershipTransfers:...", "toUserHandle": "target_handle", "expiresAt": 1730000000000 }`
- `POST /api/v1/skills/{slug}/transfer/accept`
- `POST /api/v1/skills/{slug}/transfer/reject`
- `POST /api/v1/skills/{slug}/transfer/cancel`
  - پاسخ (پذیرش/رد/لغو): `{ "ok": true, "skillSlug": "demo-skill?" }`
- `GET /api/v1/transfers/incoming`
- `GET /api/v1/transfers/outgoing`
  - ساختار پاسخ: `{ "transfers": [{ "_id": "...", "skill": { "slug": "demo", "displayName": "Demo" }, "fromUser"|"toUser": { "handle": "..." }, "message": "...", "requestedAt": 0, "expiresAt": 0 }] }`

### `POST /api/v1/users/ban`

یک کاربر را مسدود و Skillهای متعلق به او را به‌طور کامل حذف می‌کند (فقط ناظر/مدیر).

بدنه:

```json
{ "handle": "user_handle", "reason": "دلیل اختیاری مسدودسازی" }
```

یا

```json
{ "userId": "users_...", "reason": "دلیل اختیاری مسدودسازی" }
```

پاسخ:

```json
{ "ok": true, "alreadyBanned": false, "deletedSkills": 3 }
```

### `POST /api/v1/users/unban`

مسدودسازی یک کاربر را لغو و Skillهای واجد شرایط را بازیابی می‌کند (فقط مدیر).

بدنه:

```json
{ "handle": "user_handle", "reason": "دلیل اختیاری لغو مسدودسازی" }
```

یا

```json
{ "userId": "users_...", "reason": "دلیل اختیاری لغو مسدودسازی" }
```

پاسخ:

```json
{ "ok": true, "alreadyUnbanned": false, "restoredSkills": 3 }
```

### `POST /api/v1/users/reclassify-ban`

بدون لغو مسدودسازی یا بازیابی محتوا، دلیل ذخیره‌شدهٔ یک مسدودسازی موجود را تغییر می‌دهد
(فقط مدیر). مگر اینکه `dryRun` برابر با `false` باشد، حالت پیش‌فرض dry-run است.

بدنه:

```json
{ "handle": "user_handle", "reason": "هرزنامهٔ انتشار انبوه", "dryRun": true }
```

یا

```json
{ "userId": "users_...", "reason": "هرزنامهٔ انتشار انبوه", "dryRun": false }
```

پاسخ:

```json
{
  "ok": true,
  "dryRun": false,
  "userId": "users_...",
  "handle": "user_handle",
  "previousReason": "مسدودسازی خودکار بدافزار",
  "nextReason": "هرزنامهٔ انتشار انبوه",
  "changed": true
}
```

### `POST /api/v1/users/role`

نقش یک کاربر را تغییر می‌دهد (فقط مدیر).

بدنه:

```json
{ "handle": "user_handle", "role": "moderator" }
```

یا

```json
{ "userId": "users_...", "role": "admin" }
```

پاسخ:

```json
{ "ok": true, "role": "moderator" }
```

### `GET /api/v1/users`

کاربران را فهرست یا جست‌وجو می‌کند (فقط مدیر).

پارامترهای کوئری:

- `q` (اختیاری): عبارت جست‌وجو
- `query` (اختیاری): نام مستعار برای `q`
- `limit` (اختیاری): حداکثر نتایج (پیش‌فرض 20، حداکثر 200)

پاسخ:

```json
{
  "items": [
    {
      "userId": "users_...",
      "handle": "user_handle",
      "displayName": "کاربر",
      "name": "کاربر",
      "role": "moderator"
    }
  ],
  "total": 1
}
```

### `POST /api/v1/stars/{slug}` / `DELETE /api/v1/stars/{slug}`

یک نشانک را اضافه/حذف می‌کند. مسیر قدیمی `stars` و نام فیلدهای پاسخ
برای سازگاری باقی می‌مانند. هر دو نقطهٔ پایانی idempotent هستند.

پاسخ‌ها:

```json
{ "ok": true, "starred": true, "alreadyStarred": false }
```

```json
{ "ok": true, "unstarred": true, "alreadyUnstarred": false }
```

## نقاط پایانی قدیمی CLI (منسوخ‌شده)

همچنان برای نسخه‌های قدیمی‌تر CLI پشتیبانی می‌شوند:

- `GET /api/cli/whoami`
- `POST /api/cli/upload-url`
- `POST /api/cli/publish`
- `POST /api/cli/telemetry/install`
- `POST /api/cli/skill/delete`
- `POST /api/cli/skill/undelete`

برای برنامهٔ حذف، به `DEPRECATIONS.md` مراجعه کنید.

`POST /api/cli/upload-url`، `uploadUrl` و `uploadTicket` را برمی‌گرداند. انتشارهای
بسته‌ای که یک tarball از ClawPack را مرحله‌بندی می‌کنند باید شناسهٔ ذخیره‌سازی حاصل را به‌صورت
`clawpack` و ticket برگردانده‌شده را به‌صورت `clawpackUploadTicket` ارسال کنند.

## کشف رجیستری (`/.well-known/clawhub.json`)

CLI می‌تواند تنظیمات رجیستری/احراز هویت را از سایت کشف کند:

- `/.well-known/clawhub.json` (JSON، ترجیحی)
- `/.well-known/clawdhub.json` (قدیمی)

شِما:

```json
{ "apiBase": "https://clawhub.ai", "authBase": "https://clawhub.ai", "minCliVersion": "0.0.5" }
```

اگر خودمیزبانی می‌کنید، این فایل را ارائه دهید (یا `CLAWHUB_REGISTRY` را صریحاً تنظیم کنید؛ گزینهٔ قدیمی `CLAWDHUB_REGISTRY`).
