Install
openclaw skills install @oracis/skillhub-publish-skill把本地 Skill 打包并发布到 SkillHub(腾讯 skillhub.cn)与 ClawHub,覆盖预检、发布、状态回读与竞品对标全链路,并在上传被拒时用二分/ddmin 脚本把「服务端 WAF 拦内容(566)」与「包结构/字段问题」精确区分开。当用户说「发布技能到市场」「上架 skill」「SkillHub 提交失败」「Failed to fetch」「566」「上传 zip 报错」「技能审核状态」「版本号被拒」时使用。
openclaw skills install @oracis/skillhub-publish-skill发布一个技能实际要同步三个地方:GitHub(源码)、SkillHub(腾讯主市场)、ClawHub(海外市场)。 本技能覆盖全链路,并在任何一步被拒时给出可定位根因的判定,而不是猜。
上传失败的报错只有四类,每类的对策完全不同,先看报错文案归位:
| 报错文案 | 类别 | 对策 |
|---|---|---|
不支持的文件类型: xxx | 包内容 | 用 scripts/publish.py 发(它按白名单自动过滤) |
字段格式校验 类 | 包内容 | 跑 scripts/preflight.py 查 frontmatter |
slug 已被占用 / 409 / 500 | slug 冲突 | publish.py 会自动追加后缀重试并回写 SKILL.md |
提交失败 / Failed to fetch / net::ERR_FAILED 566 / CORS 报错 | 服务端 WAF 拦内容 | 跑 scripts/waf_bisect.py 定位,改内容,别改结构 |
不要把第四类当成网络/代理/浏览器扩展问题。判据是客观的:用本机脚本向发布端点 POST 同一份 multipart,
401 就说明请求已到达应用层(正常,只差登录态),566 才是被 WAF 拦。
这一条是本技能与其他发布类技能的核心差别。其他工具只处理 400/409/429, 遇到 566 会误导你去「检查元数据与文件类型」——方向完全错了。
判断「某个技能有没有」「上架了没有」之前,先确认你用的接口能看到什么范围。 公开接口天然过滤掉未公开、审核中、已下架的条目 —— 拿它当「是否存在」的判据会得到假阴性。
| 想知道什么 | 用哪个 | 别用哪个 |
|---|---|---|
| 这个技能公开发布了吗 | status <slug>(公开搜索) | — |
| 我到底发过哪些(含测试残留) | mine(我的后台) | ❌ 公开搜索查不到 |
| 某个 slug 归谁 | status / 看 namespace.handle | — |
之前踩过的具体坑:用公开搜索查自家测试探针,搜不到就判定「已被平台自动清理」,
实际 18 个条目里 12 个探针都还挂在「我的 Skills」里。清点自己发了什么,永远用 mine。
python scripts/preflight.py <skill目录> # 人读报告
python scripts/preflight.py <skill目录> --json # Agent 解析
三级输出:ERROR(会被拒,必须修)/ WARN(能过审但影响质量)/ INFO(发布操作提醒)。 存在 ERROR 时退出码为 1,可直接接发布脚本卡点。
覆盖:frontmatter 缺字段与非法值、位图与独立 LICENSE 拒收、包结构与白名单、密钥泄漏、安全红线、 占位符残留、WAF 命中特征(本脚本独有)。
python scripts/publish.py validate <skill目录>
缺 slug / version / displayName 会明确列出。补进 SKILL.md frontmatter——这些字段不影响 skill 本地运行。
python scripts/publish.py publish <skill目录> --version 1.4.0 --dry-run
打印将要上传的文件清单、体积、图标识别结果、被排除的文件及原因,
以及与线上版本的比对结果(versionCheck: OK / SAME / TOO_OLD)。确认无误再真发。
python scripts/publish.py publish <skill目录> --version 1.3.0 --changelog "变更说明" \
[--icon icon.png] [--category dev-programming] [--subcategory dev-script]
脚本自动完成:白名单过滤 → 图标两步上传 → 分类 key 校验 → 429 指数退避 → slug 冲突自愈。
成功返回 skillId / versionId / slugUsed / category / subCategories;
失败会打印分类结论 + 下一步动作。
分类建议写进 SKILL.md frontmatter(比分次传参可靠,下次发版自动带上):
category: dev-programming
subCategories: [dev-script, dev-code-gen]
不填分类的话平台会显示「未分类」——注意「选填」不等于可以不管,
市场里未分类的条目曝光会明显吃亏。可选值用 publish.py categories 查。
带图标时务必确认响应里 iconAuditStatus: "pending" —— 若是 null 说明图标没收到
(那通常意味着把图片错当成 multipart part 传了,见上方「图标」注意事项)。
分类则看响应里有没有回显 category 字段。
python scripts/publish.py status <skill目录> # 传目录会自动比对本地/线上版本
python scripts/publish.py status <slug> # 只查线上
输出线上版本、下载/安装/收藏数、更新时间,并给出同步状态:
| 同步状态 | 含义 |
|---|---|
✓ 已同步 | 本地版本 == 线上版本,审核已通过 |
↑ 本地更新 | 本地版本更高 —— 还没发,或已提交仍在审核中 |
✗ 版本倒挂 | 本地版本 ≤ 线上版本 —— 发上去会被服务端拒 |
⚠️ search 索引有延迟:刚发布(还在审核)的版本不会立即出现在查询结果里, 可能仍显示上一个线上版本。这是正常的,不是发布失败。
⚠️
status走的是公开搜索接口,看不到未公开条目。要清点「我到底发过什么」 (含审核中、已下架、测试残留),用mine命令 —— 那是另一个接口(/api/v1/dashboard/skills)。
python scripts/publish.py mine # 列出我名下全部技能(含线上搜索看不到的)
python scripts/publish.py mine --json # 机器可读
python scripts/publish.py rm <slug> # 只提示,不执行(安全护栏)
python scripts/publish.py rm <slug> --yes # 确认删除:自动「先下架 → 再删除」
mine 与 status 的区别(踩过坑,别搞混):
| 命令 | 数据源 | 看得到什么 |
|---|---|---|
status <slug> | 公开搜索 /api/v1/search | 只有已公开条目;未过审/已下架的查不到 |
mine | 我的后台 /api/v1/dashboard/skills | 我发的全部:审核中、已下架、测试残留都在 |
删除是两步(服务端强制):直接 DELETE 会返回 409 只能删除已下架的 Skill,请先下架。
rm 已自动串好 /unlist → DELETE,并对「已下架再删」的重复调用做了容错。
删除不可恢复,所以默认不给 --yes 就只打印风险提示、不执行。
clawhub skill publish <skill目录> \
--slug <slug> --name "<显示名>" \
--categories "<14 项之一>" --topics "a,b,c" \
--changelog "..."
--source-repo / --source-commit 成对给或都不给,只给一个报 must be provided together。
两条容易白费功夫的约束(详见 references/clawhub-catalog.md):
--icon/--logo 参数,SKILL.md 里放
<slug>.png 对它无效。别为 ClawHub 折腾图标链路。developer-tools ❌ → 正确是 development。
传错报 Unknown skill category slug;超过 3 个报 Categories are limited to 3。--categories/--topics 就一定发新版本(即使文件没改)——这是给
已发布技能补分类的官方路径,CLI 会自动 bump patch。见 references/github-repo.md。本机已有可用凭据,不需要向用户索要 PAT。
服务端要求更新时 version 必须严格高于线上版本(相同或更低都会被拒)。
publish 命令会在发布前自动比对并拦下,避免白发一次:
{ "success": false, "blockedBy": "version_check",
"localVersion": "1.2.9", "remoteVersion": "1.3.0",
"error": "版本号 1.2.9 比线上 1.3.0 更低,服务端会拒绝更新",
"fix": "把版本号改成高于 1.3.0 的值(如升到 1.3.1),或加 --force 跳过此检查" }
--force。python scripts/publish.py compare "<关键词>"
按下载量列出同类技能,用客观数据判断投入方向 —— 比凭感觉判断「我们有什么优势」可靠:
关键词「skill 发布」同类技能(按下载量排序)
版本 下载 安装 技能
1.2.1 998 26 ClawHub Skill 发布避坑指南 @clawhub_tudoubudou
1.0.1 83 0 开源发布(GitHub Skill 发布器) @user_73cb5b59
下载量反映曝光面,安装量反映真实使用。
发布前对齐六个地方的 slug/name:本地目录名、SKILL.md name、SKILL.md slug、manifest name、
GitHub 仓库名、ClawHub --slug。
SkillHub 已发布的 slug 不可改(更新对话框里只读、无改名入口),所以它以 SkillHub 为准当「锚」, 其余平台向它对齐,不要新建重复 Skill。
| 文件 | 什么时候读 |
|---|---|
scripts/preflight.py | Step 1;发布前必跑 |
scripts/publish.py | Step 2-6;日常发布主入口(含 status / compare / mine / rm / categories) |
scripts/waf_bisect.py | 出现 566 / Failed to fetch 时定位根因 |
references/waf-details.md | 需要理解 WAF 命中特征、判据原理与规避写法时 |
references/icon-upload.md | 要传图标/封面,或怀疑图标没生效(iconAuditStatus: null)时 |
references/categories.md | SkillHub 要填/补分类,或后台显示「未分类」时 |
references/clawhub-catalog.md | ClawHub 要填分类/话题,或问 ClawHub 要不要图标时(14 个可用 slug) |
references/path-traps.md | 目录路径没生效、slug 被自动改写,或同名仓库不是扁平技能仓库时 |
references/cli-install.md | 需要安装/修复官方 CLI,或走网页 CDP 兜底路径时 |
references/github-repo.md | 需要建 GitHub 仓库并推送时 |
手头没有现成图标? 用 zero-dep-icon-gen 技能本地生成
(零依赖手写 PNG,不装 Pillow,一条命令出图):
PY="C:/Users/DELL/.workbuddy/binaries/python/versions/3.13.12/python.exe"
$PY ~/.workbuddy/skills/zero-dep-icon-gen/scripts/gen_icons.py \
--spec "my-skill:#4F46E5,#818CF8,browser" -o my-skill/
# 生成 my-skill/my-skill.png,文件名 = slug,find_icon() 会自动兜底找到
可用符号:browser cloud shield folder check download
arrow-up gear search lock text(点阵文字)。--list-symbols 看全量。
SKILL.md / manifest.yaml / scripts/*.py。
.gitignore / LICENSE / README 会被拒。别用 GitHub「Download ZIP」(会带上那几个文件)。<slug>.zip(不带版本号)。≤10MB。POST /api/v1/community/skill-icons/upload(multipart 字段名 file)拿 iconUrl,
再放进 payload.iconUrl 提交。把图片直接当作 cover/icon part 会被服务端静默忽略
—— 发布仍返回 201,但 iconAuditStatus 恒为 null(pending 才是生效)。
publish.py --icon xxx.png 已封装这条链路,上传失败只告警不阻断发布。os.path.join 在 Windows 产生反斜杠会导致 KeyError):import zipfile, os
TOP = "<slug>"
with zipfile.ZipFile(out, "w", zipfile.ZIP_DEFLATED) as z:
for rel in ["SKILL.md", "manifest.yaml"] + ["scripts/" + f for f in os.listdir("scripts") if f.endswith(".py")]:
z.write(os.path.join(repo, rel), TOP + "/" + rel)
skh_ / gho_ / ghp_ 开头一律脱敏)。waf_bisect.py 给出的是定位方法而非稳定规则;
命中特征会变化,按「二分定位」的思路排查,别背特征表。本技能里最贵的两个结论(WAF 566 的真身、图标的两步链路)都不是文档里写的, 而是打真实请求试出来的。踩过的坑值得记住:
cover part 返回 201,但字段被静默忽略。
判断字段是否生效,要看响应体里对应的状态字段(如 iconAuditStatus),
而不是看 HTTP 状态码。references/icon-upload.md 的「反查方法」),比猜字段名快得多。