# AstrBot 插件市场 JSON 规范

规范版本：2026-06-27

插件市场 JSON Schema Version：1

## 1. 范围

本文定义 AstrBot 插件市场源的 JSON 格式。

## 2. 术语

- `PluginRecord`：插件市场源中的一个插件记录。
- `MarketMetadata`：插件市场源自身的元数据。
- `plugin_id`：全局插件包标识。
- `metadata.yaml`：已安装插件包内的插件元数据文件。

本文中的“必须”“不得”“应该”“不应该”“可以”均为规范性要求。

## 3. 文档格式

插件市场源必须是 UTF-8 JSON 对象。

根对象必须包含保留 key `$meta`。

除 `$meta` 外，所有根 key 必须是以下之一：

- `plugin_id` 字符串
- 第 5 节兼容例外中的 `name` 字符串

除 `$meta` 外，每个根 key 必须映射到一个 `PluginRecord` 对象：

```json
{
  "$meta": {
    "schema_version": 1,
    "name": "AstrBot Official Plugin Market",
    "version": "2026-06-27",
    "homepage": "https://astrbot.app",
    "repository": "https://github.com/AstrBotDevs/AstrBot_Plugins_Collection"
  },
  "owner/astrbot_plugin_foo": {
    "author": "owner",
    "name": "astrbot_plugin_foo",
    "version": "1.0.0",
    "repo": "https://github.com/owner/astrbot_plugin_foo",
    "desc": "Plugin description"
  },
  "astrbot_plugin_bar": {
    "author": "owner",
    "version": "1.0.0",
    "repo": "https://github.com/owner/astrbot_plugin_bar",
    "desc": "Compatibility record using the root key as name"
  }
}
```

根对象不得包含 `$meta` 之外的顶层元数据字段。

## 4. 市场元数据

`$meta` 不得被解释为 `PluginRecord`。

`$meta` 必须是 `MarketMetadata` 对象。

`MarketMetadata` 必须包含：

| 字段 | 类型 | 要求 |
| --- | --- | --- |
| `schema_version` | integer | 插件市场 JSON schema 版本。本规范中必须为 `1`。 |

`MarketMetadata` 可以包含：

| 字段 | 类型 | 要求 |
| --- | --- | --- |
| `name` | string | 插件市场源的人类可读名称。 |
| `version` | string | 插件市场源数据版本。不得用作 schema 版本。 |
| `homepage` | string | HTTPS 主页 URL。 |
| `repository` | string | 插件市场源数据的 HTTPS 仓库 URL。 |
| `description` | string | 插件市场源描述。 |
| `updated_at` | string | ISO 8601 时间戳。 |

`$meta` 不得用于：

- 插件身份
- 安装源解析
- 更新源解析
- 插件安全校验

## 5. 插件身份

`plugin_id` 定义为：

```text
metadata.author + "/" + metadata.name
```

对每个 `PluginRecord`：

- 根 key 应该等于 `${author}/${name}`。
- 根 key 可以在下方兼容例外中等于 `name`。
- `author` 必须等于 `metadata.yaml.author`。
- `name` 必须等于 `metadata.yaml.name`。
- `plugin_id` 必须在 AstrBot 插件生态内全局唯一。
- 两个按小写归一化后相等的 `plugin_id` 不得同时存在。
- `repo` 不得用作插件身份。
- `root_dir_name`、本地目录名、展示名、registry 名称不得用作插件身份。

`author` 和 `name`：

- 必须是非空字符串。
- 必须去除首尾空白。
- 不得包含 `/`。
- 不得包含 ASCII 控制字符。
- 应该是稳定的包身份值，而不是展示名。

### 5.1. Name Key 兼容例外

根 key 可以等于 `metadata.yaml.name`，而不是 `${author}/${name}`。

使用该例外时：

- 根 key 不得包含 `/`。
- `author` 必须存在。
- `name` 可以省略。
- 如果 `name` 存在，必须等于根 key。
- 根 key 必须等于 `metadata.yaml.name`。
- 消费者必须将 `plugin_id` 构造为 `${author}/${root_key}`。

## 6. 必填字段

每个 `PluginRecord` 必须包含：

| 字段 | 类型 | 要求 |
| --- | --- | --- |
| `author` | string | 插件作者命名空间。必须匹配 `metadata.yaml.author`。 |
| `name` | string | 插件包名。必须匹配 `metadata.yaml.name`。仅当根 key 等于 `metadata.yaml.name` 时可以省略。 |
| `version` | string | 插件版本。必须匹配 `metadata.yaml.version`。 |
| `repo` | string | 用于来源归属和安装的 HTTPS GitHub 仓库 URL。 |
| `desc` | string | 默认插件描述。 |

## 7. 可选字段

| 字段 | 类型 | 要求 |
| --- | --- | --- |
| `display_name` | string | 人类可读插件名称。 |
| `short_desc` | string | 紧凑 UI 使用的短描述。 |
| `download_url` | string | HTTPS ZIP 下载 URL。已安装包身份仍必须匹配 `author/name`。 |
| `logo` | string | HTTPS 图片 URL 或相对静态资源路径。 |
| `tags` | string[] | 标签列表。 |
| `category` | string | 市场分类 key。 |
| `support_platforms` | string[] | 支持的平台 key。 |
| `astrbot_version` | string | AstrBot 版本要求表达式。 |
| `social_link` | string | 作者或项目 HTTPS URL。 |
| `updated_at` | string | ISO 8601 时间戳。 |
| `i18n` | object | 本地化展示字段。 |
| `pinned` | boolean | 市场推荐标记。 |
| `stars` | integer | 非负 star 数或排序信号。 |
| `download_count` | integer | 非负下载数。 |

### 7.1. 仓库 URL

`repo` 必须是以下之一：

```text
https://github.com/{owner}/{repository}
https://github.com/{owner}/{repository}.git
https://github.com/{owner}/{repository}/tree/{branch}
```

`owner`、`repository`、`branch` 必须匹配：

```text
[A-Za-z0-9_-]+
```

`repo` 不得是：

- HTTP URL
- SSH URL
- 非 GitHub URL
- GitHub Enterprise URL
- GitHub 文件、release、pull request、issue 或子目录 URL

`download_url` 可以指向任意 HTTPS ZIP URL。

### 7.2. 本地化字段

`i18n` key 应该是 BCP 47 locale tag。

每个 locale 对象可以包含：

```json
{
  "display_name": "Localized name",
  "desc": "Localized description",
  "short_desc": "Localized short description"
}
```

## 8. 保留字段

插件市场源不得在 `PluginRecord` 对象中使用这些字段：

| 字段 | 原因 |
| --- | --- |
| `plugin_id` | 由 `author/name` 派生。 |
| `market_plugin_id` | 运行时/客户端兼容字段，不是市场数据。 |
| `market_plugin_identifier` | 已弃用的运行时/客户端兼容字段。 |
| `root_dir_name` | 本地安装字段。 |
| `local_plugin_name` | 本地安装字段。 |
| `install_method` | 本地安装字段。 |
| `registry_url` | 本地安装字段。 |
| `registry_name` | 本地安装字段。 |
| `installed_at` | 本地安装字段。 |

消费者不得使用保留字段作为插件身份。

根 key `$meta` 保留给 `MarketMetadata`。

## 9. 已弃用字段

迁移期间客户端可以接受这些字段，但新的市场源不得输出它们：

| 已弃用字段 | 替代字段 |
| --- | --- |
| `support_platform` | `support_platforms` |
| `platform` | `support_platforms` |

## 10. 未知字段

消费者可以保留未知字段。

消费者不得将未知字段用于：

- 插件身份
- 安装源解析
- 更新源解析
- 安全校验

## 11. 校验规则

市场源仅在所有规则通过时有效：

1. 根 JSON 值是对象。
2. `$meta` 存在。
3. `$meta` 是对象。
4. `$meta.schema_version` 是整数 `1`。
5. 除 `$meta` 外，每个根 key 是 `plugin_id` 或 `name` key。
6. 除 `$meta` 外，每个根 value 是 `PluginRecord` 对象。
7. 每条记录包含所有必填字段；仅当根 key 是 `name` key 时，`name` 可以省略。
8. 每个必填字符串字段去除首尾空白后非空。
9. `author` 和存在的 `name` 不包含 `/`。
10. 根 key 精确等于 `${author}/${name}`，或在兼容例外下精确等于 `name`。
11. 不存在两个小写归一化后相等的 ID。
12. `repo` 是 HTTPS GitHub 仓库 URL。
13. 如果存在，`download_url` 是 HTTPS URL。
14. 如果存在，`tags` 是字符串数组。
15. 如果存在，`support_platforms` 是字符串数组。
16. 如果存在，`stars` 是非负整数。
17. 如果存在，`download_count` 是非负整数。
18. 如果存在，`updated_at` 是 ISO 8601 时间戳。
19. 如果存在，`$meta.homepage` 是 HTTPS URL。
20. 如果存在，`$meta.repository` 是 HTTPS URL。
21. 如果存在，`$meta.updated_at` 是 ISO 8601 时间戳。
22. 从 `repo` 或 `download_url` 解析出的包包含 `metadata.yaml`。
23. `metadata.yaml.author` 等于 `author`。
24. `metadata.yaml.name` 等于 `name`。
25. `metadata.yaml.version` 等于 `version`。

## 12. 安装规则

市场安装：

1. 客户端向后端发送选中的 `plugin_id` 和 registry source。
2. 后端从该 registry source 中按 `plugin_id` 解析 `PluginRecord`。
3. 如果 `download_url` 存在，后端使用 `download_url` 安装；否则使用 `repo` 安装。
4. 安装后，后端读取已安装的 `metadata.yaml`。
5. 已安装的 `metadata.yaml.author/name` 必须等于选中的 `plugin_id`。
6. 如果身份校验失败，安装必须失败。
7. 市场安装成功后，可以在本地状态中持久化 `repo`、`download_url`、registry 信息和安装时间。

## 13. 更新规则

市场安装的插件：

1. 更新检测必须使用安装时记录的 registry source。
2. 更新检测必须按 `plugin_id` 解析市场记录。
3. 版本比较必须使用已安装 metadata 版本和市场记录 `version`。
4. 执行更新必须使用解析出的市场记录 `repo/download_url`。

非市场安装：

1. URL 安装、Git 安装、上传安装不得被视为市场安装。
2. 可以从已安装的 `metadata.yaml` 持久化 `plugin_id`。
3. 除非显式绑定到市场源，否则不得启用市场更新检测。

## 14. 源绑定规则

将已安装的非市场插件绑定到市场源时：

1. 必须从已安装的 `metadata.yaml` 读取插件身份。
2. 候选市场记录必须具有相同 `plugin_id`。
3. 候选市场记录应该具有相同仓库身份。
4. 绑定必须持久化选中的 registry source 和 `plugin_id`。

## 15. 兼容性

迁移期间，消费者可以读取旧版市场源。

对于旧版源：

- 迁移期间，消费者可以接受没有 `$meta.schema_version` 的源。
- 消费者应该从 `author/name` 构造 `plugin_id`。
- 消费者应该拒绝缺少 `author` 或 `name` 的记录用于市场安装。
- 消费者可以仅将旧版根 key 用于展示。
- 除非 key 等于 `${author}/${name}`，消费者不得将旧版根 key 用作插件身份。

新的市场源必须包含 `$meta.schema_version`。

