发布 Skill 到国内 SkillHub(skillhub.cn)
面向 国内 SkillHub(skillhub.cn,腾讯云基于 OpenClaw 生态做的本土化技能社区)的发布流程手册。
指挥 agent 把本机的一个 skill 目录发布成社区技能,并在发布前后做足校验——
这个流程里最大的坑是「预检通过 ≠ 能发」「刚发完看起来没生效」。
〇·〇 先说清楚:发到哪个 SkillHub?
生态里有两个容易混淆的平台,别搞混,本手册只覆盖第一个:
| 国内 SkillHub | ClawHub | |
|---|---|---|
| 站点 / CLI | https://skillhub.cn,CLI 命令 skillhub |
OpenClaw 官方的全球源社区,CLI 不叫 skillhub |
| 定位 | 腾讯云做的本土化镜像站,国内节点加速、中文搜索 | 官方源站,面向全球 |
| 命名空间观感 | 本站注册用户是 @user_xxxx/… |
从官方源同步过来的技能是 @clawhub_xxxx/… |
| 本手册是否覆盖 | ✅ 就是它 | ❌ 不覆盖 |
判断"我到底在往哪发"的办法:看 CLI 的 --host,本手册全程固定 https://api.skillhub.cn;
发布成功的详情页一定是 https://skillhub.cn/skills/<slug>。
若目标其实是 ClawHub,先别照着这份手册走——那是另一个平台、另一套 CLI。
〇、环境准备
- 站点
https://skillhub.cn;API host 固定https://api.skillhub.cn。 - CLI 命令名
skillhub,确认它在 PATH 里:
bash command -v skillhub || echo "未安装/不在 PATH" - ⚠️ 非交互 shell(agent、CI、脚本)不会加载
~/.zshrc。如果 CLI 装在~/.local/bin,
每条命令前都要显式补 PATH,否则一路command not found:
bash export PATH="$HOME/.local/bin:$PATH" - 个人 API Token 形如
skh_...,登录命令:
bash skillhub login --key skh_xxx --host https://api.skillhub.cn skillhub auth whoami # 查当前登录身份
登录态落盘在~/.skillhub(凭证文件~/.skillhub/credentials.json)。 - 其他常用子命令:
skillhub auth(管理个人 Token)、skillhub verify(校验本机 skill 与平台签名是否一致)、
skillhub self-upgrade(自升级)。
一、发布前必须补齐 SKILL.md 的 frontmatter
本地自用只需 name / description / agent_created;要发布,还得有下面这几个——
它们只有跑 dry-run 时才报错,所以别等正式发布才发现:
slug: my-skill # 全网唯一,冲突报 409
displayName: 中文展示名 # 对外的名字
version: 1.0.0
summary: 一段简介 # 建议比 description 短
license: MIT
⚠️ slug 发布后不要再改——改了相当于新建一个技能,历史版本和详情页都会断掉。
更新时保持 slug 不变,只递增 version 和改正文。
⚠️ description 别写成一整行超长字符串:部分 YAML 解析器会拒绝加载单行长 description。
用块标量写成多行更稳:
description: >
第一行……
第二行……
⚠️ 本地自用字段别直接搬去发布:name_display 之类自造字段平台不认,要用 displayName;
name 与 slug 都留着最省事。另外 dry-run 是逐条报错的(报完「缺少 slug」就停下),
补齐一项得再跑一次,别指望一次修完。
二、发布前隐私脱敏(必做,别跳)
本地自用 skill 里往往沉淀了真实环境上下文(真实域名、内网 IP、绝对路径、账号 handle),
发到公开社区前先扫一遍。改副本、别改自用版——自用版留着真实信息反而方便 AI 认环境:
grep -rniE "你的域名|你的IP|@[0-9]{1,3}(\.[0-9]{1,3}){3}|/Users/[a-z]+|sshpass|password|\.pem" <skill目录>
重点看三处:
references/里的「来源 / 实战背景」段落——最爱写真实域名和内网拓扑;scripts/里的凭据变量;- 正文示例里粘的真实 URL、真实账号 handle。
⚠️ 明文密码类脚本(例如用 expect 传 SSH_PASS)即使换成占位符,
也可能被判「教用户把密码写进脚本」——正文要把密钥登录列为默认方案,
明文脚本降级为备选并显式加一句风险提示。
三、预检 → 发布(两条命令,别跳预检)
export PATH="$HOME/.local/bin:$PATH"
skillhub publish <skill目录的绝对路径> --dry-run
skillhub publish <skill目录的绝对路径> --changelog "本次改了什么"
path可以是 skill 目录(内含SKILL.md)或.zip;目录下的子文件(scripts/、references/)会一起打包。- 成功输出:
✓ Dry-run passed: <slug>@<version>→✓ Published: skillId=xxxxx status=... --json拿结构化输出;--version可临时覆盖版本号;--token/--host可临时覆盖登录态。
四、发布后怎么核查
- 详情页
https://skillhub.cn/skills/<slug>。刚发布时可能短暂显示「审核中」,稍等即可访问。 - ⚠️ 详情页是 SPA:
curl抓回来只有几 KB 的空壳,抓不到任何内容。别用它验证发布结果。 - ⚠️
skillhub search <关键词>搜不到刚发布的技能(还在审核队列里),别据此判断发布失败。 - 安装命令:
skillhub install <slug> --namespace <handle>。
查服务端真实数据的唯一可靠办法(GET /api/v1/skills/<slug>,需带 token):
python3 - <<'PY'
import json, os, urllib.request
tok = json.load(open(os.path.expanduser('~/.skillhub/credentials.json')))['user']['token']
req = urllib.request.Request('https://api.skillhub.cn/api/v1/skills/<slug>',
headers={'Authorization': 'Bearer ' + tok})
d = json.load(urllib.request.urlopen(req, timeout=25))
for k in ['displayName', 'summary', 'slug']:
print('skill.' + k, '=', d['skill'][k])
print('tags.latest =', d['skill']['tags']['latest']) # 已上传到的最新版本号
print('versions =', d['skill']['stats']['versions']) # 服务端已收到的版本数
print('安全扫描 =', {k: v['statusText'] for k, v in d['securityReports'].items()})
PY
⚠️ 改 displayName / summary 不会立刻在页面上生效:服务端要等新版本过完安全扫描与审核
(期间 securityReports.*.status = queued「排队中」),通过后才把展示元数据切到新版。
这中间会出现「看起来没更新」的假象——tags.latest 已是新版本号、stats.versions 已 +1,
但 skill.displayName / skill.summary 还停在旧值。这不是发布失败,别急着重发。
顺手:怎么判断某个 slug 还没被占
curl -s "https://api.skillhub.cn/api/v1/skills/<slug>" | grep -q '"error"' && echo FREE || echo TAKEN
⚠️ 该接口未命中时也返回 HTTP 200(错误信息在 body 的 "error" 字段里),
所以判断依据是 body,不是 HTTP 状态码——只看状态码会误判成「已被占用」。
五、常见报错对照
| 报错 | 处理 |
|---|---|
command not found: skillhub |
命令前补 export PATH="$HOME/.local/bin:$PATH" |
Error: 未登录 |
先 skillhub login --key skh_... --host https://api.skillhub.cn |
401 invalid api key |
token 失效/被撤销,去站点 /dashboard/keys 重建后重新 login |
403 请先完成实名认证 |
让用户去网页端做实名认证,agent 侧无法代做 |
409 slug 已被其他用户占用 |
换一个 slug 重发(先用上一节的接口确认空闲) |
429 请求过于频繁 |
等约 1 分钟重试 |
Error: 请求失败 (400): 不允许的文件类型: scripts/xxx.exp |
脚本扩展名被平台拒。⚠️ dry-run 不检查文件类型,只有正式发布才校验——预检通过 ≠ 能发。改成允许的扩展名(实测 .sh 可过),并在文件头注明真实解释器(例如 expect/Tcl 脚本套 .sh,必须写明「用 expect 调用,不能用 bash」) |
六、dry-run 的检查盲区(最容易翻车的地方)
--dry-run 只做两件事:校验 frontmatter 必填字段 + 打包。它不校验文件类型白名单,
也不跑安全扫描。所以用下面这个顺序兜底:
- dry-run 过 → 再正式发布(正式发布才会暴露文件类型问题)。
- 返回
✓ Published: skillId=...后,必须用第四节的接口查一次
securityReports.*.statusText——安全扫描是异步的,刚发完通常全是「排队中」,
真出问题只能从这里看到。 - 遇到 400 文件类型报错,直接换扩展名重发即可,不需要动 slug 或 version
(失败的发布会占用版本号,重发不受影响)。
七、更新已发布技能的流程
- 改本机 skill 目录下的
SKILL.md(和外挂的scripts//references/)。 - 递增
version,slug保持不变。 - 回到第三节跑两条命令(
--dry-run→ 正式发),--changelog写清本次改了什么。 - 回到第四节核查服务端数据。