# 发布与更新

> 在 GitHub 上分享卡片、用户可以用哪些地址安装、安装如何锁定、更新如何送达用户以及何时需要再次同意，还有卡片与协议的版本管理。

Source: https://docs.neurosquad.ai/zh/card-sdk/publishing

没有商店：**卡片放到 GitHub 上就算发布了**，用户通过它的 GitHub 地址安装。审核是可选的——如果希望卡片进入应用的
[已验证目录](https://docs.neurosquad.ai/zh/card-sdk/verified-cards)（可在添加菜单中搜索，并带有“已验证”标记），请按
[SUBMITTING.md](https://github.com/glmn-ai/neurosquad-cards/blob/HEAD/docs/SUBMITTING.md) 的说明向 `glmn-ai/neurosquad-cards` 提交 pull request。每个审核过的版本都固定在一个提交上。

## 发布

1. 运行 `npx @neurosquad/card-sdk validate` 和 `pack --dry-run`。修复所有错误；以陌生人的眼光读一遍安装对话框预览。
2. 提交卡片所需的一切——包括构建产物（`dist/`），因为应用从不构建任何东西。`pack` 会准确列出将被安装的内容。
3. 推送到一个公开的 GitHub 仓库（私有仓库也可以，供添加了 GitHub 令牌的用户使用）。
4. 可选：打一个发布标签：`git tag v1.0.0 && git push --tags`，并基于该标签创建 GitHub release。
5. 把地址告诉大家。

**一个仓库里放多张卡片**也没问题：每张卡片放在自己的文件夹里，有自己的清单，用 `owner/repo/path/to/folder` 安装。

## 用户可以从哪些地址安装

| 地址 | 安装的是 | 更新跟随 |
| --- | --- | --- |
| `owner/repo` | 默认分支的最新提交 | 默认分支 |
| `owner/repo@main` | 该分支 | 该分支 |
| `owner/repo@v1.2.0` | 该标签 | 不更新——标签是固定的 |
| `owner/repo@<40-hex commit>` | 该提交 | 不更新 |
| `https://github.com/owner/repo/releases/tag/v1.2.0` | 该发布版 | **最新发布版** |
| `owner/repo/cards/pomodoro`、`https://github.com/owner/repo/tree/main/cards/pomodoro` | 该文件夹中的卡片 | 同上 |

`github.com/owner/repo`、`https://github.com/owner/repo.git` 也可以。包含 `/` 的 ref 必须使用 `@ref` 形式。

**锁定到提交。** 无论用哪种地址，应用都会把它解析为一个提交，并安装该提交的文件。用哈希指定的提交必须能从仓库的默认分支到达——
只存在于某个分叉中的提交会被拒绝——而改过名或转移过的仓库必须用它当前的名称安装。应用会记录该提交以及所有文件的树哈希。
用户输入的地址并不是卡片的身份——`github:owner/repo[/folder]` 才是——因此更新会保留卡片的权限、存储以及它在所有画布上的每一份副本。

## 更新如何送达用户

- 应用在启动一分钟后检查，之后每 24 小时检查一次，用户点击 **Check for updates** 时也会检查。
- 发现更新的提交后，会先下载并校验，然后提供给用户：设置中的 **Update** 和卡片上的一个小圆点。**不会自动应用任何更新。**
- 用户会看到变化：新版本号、旧 → 新的提交以及指向 GitHub 比较页面的链接，还有权限差异——
  - **新权限**和**新网络主机** → 更新会等待用户同意；
  - **不再需要的权限** → 更新时撤销；
  - 新的**可选**权限 → 列为“之后可能会请求”，永远不会自动授予；
  - 新增或措辞改变的**工具**、新增或改了类型的**端口**、新的**密钥设置**、变更的 **displayName、作者或主页** → 更新同样会等待用户同意。
- 更新时，卡片每一份打开的副本都会以 `launch === 'updated'` 重新加载。存储和设置会保留——如果你改了它们的结构，请在代码中迁移。
- 如果某个清单在更新后的应用中不再通过校验，卡片会显示为损坏并注明原因；它无论如何都不会被运行。

> 强制推送标签或改写历史并不能把代码偷偷塞给用户：重新下载同一个提交却得到不同内容时会被拒绝，而且每次更新在应用前都会展示出来。
> 但用户每接受一次更新，就是在信任你一次——请保护好你的 GitHub 账号（开启双重验证），并像对待会在别人电脑上运行的代码那样审查对你卡片的拉取请求。

## 版本管理

**卡片的 `version`** 仅供参考——安装锁定的是提交——但用户会在安装对话框、更新对话框和 **Settings → Custom cards** 中看到它。
请使用 SemVer，并在每次发布时递增。对端口或工具的破坏性修改要写进描述：别人的卡片和智能体的使用习惯可能依赖它们。

**`minAppVersion`** 会阻止卡片安装到版本太旧的应用中。

**协议**是单独管理版本的：你清单中的 `"protocol": 1`，SDK 中的 `CARD_PROTOCOL_VERSION`。

- 新的方法、事件和可选字段会在协议 1 **之内**陆续加入。用 `card.host.supports('method.name')` 做功能检测，没有它们时也要能继续工作。
- 破坏性变更会成为协议 2。只支持协议 1 的应用会拒绝为协议 2 开发的卡片，并提示“needs a newer NeuroSquad”；应用会在至少一年内同时继续支持协议 1 的卡片。
- 在同一个主版本内升级 `@neurosquad/card-sdk` 是安全的；重新构建并发布即可。

## 修改权限和卡片提供的内容

新增一项必需权限或一个网络主机——或者一个工具、一个端口、一个密钥设置，或修改卡片的名称、作者或主页——意味着每个现有用户都必须接受这次更新，才能得到其中的**任何**内容——包括 bug 修复。可以考虑：

- 把新权限设为**可选**，在用户真正要用这个功能时再申请；
- 把权限变更单独作为一次发布，并在描述中说明。

## 弃用卡片

请保留仓库：已安装的副本运行时不需要它，但更新和重新安装需要。在描述中说明情况，并指向替代品。
