Install
openclaw skills install @huaweiclouddev/huawei-cloud-publish-work-to-galleryPublish user's work to the Huawei Cloud University Operations Platform (华为云高校运营平台/作品陈列馆). Use this skill whenever the user wants to publish, submit, or upload a project/work to the gallery or a training camp (训练营) on the platform — including casual phrasings like "把作品发布上去", "投稿到陈列馆", "传作品到平台", "提交作品/项目", "报名发布作品", as well as formal ones like "publish to work gallery", "submit to training camp", "upload work to the platform". Do NOT use for general dev questions, git push to GitCode alone, or platform browsing without publishing intent.
openclaw skills install @huaweiclouddev/huawei-cloud-publish-work-to-gallery发布作品至华为云高校运营平台。确定性子任务优先调用内置脚本(单入口、fail-fast、自带 --help),stdout/退出码即判定依据。脚本报错才查 references/troubleshooting.md。
Step 1 (workDir, domainID, STS)
Step 2 (port→变量, envUrl→变量) ← 依赖 Step 1
├── Step 3 (gitUrl, gitBranch, workName) ← 只依赖 workDir
├── Step 5 (introduction, camps) ← 只依赖 workDir + STS
├── Step 4 (coverImage) ← 依赖 port + workName
└── Step 6 (detailZip) ← 依赖 workName + 代码分析
Step 7 (trainingCampId) ← 依赖 Step 5 camps
Step 8 (publish) ← 依赖以上全部
并行(Step 2 完成后):Step 3/4/5/6 互不依赖,四者全并行(取最长路径 Step 4 ~90s,而非串行 ~160s)。Step 4 内 preflight + ensure-utf8 再并行。
变量贯穿:port/workName/envUrl 在最早步骤确定后作为变量传递全流程,禁止硬编码(隧道 URL 含 port,换端口时 envUrl 随之变)。
执行模型:① 脚本契约优先——按 stdout 成功信号判定,成功路径零源码读取;失败才 脚本提示 → troubleshooting 对应节 → grep 脚本源码 逐级查 ② 禁止手工绕过门禁(publish-work.mjs 内置 verifyGates() 复核)③ 网络超时统一 3s ④ detect-env.mjs 确定平台后只读对应平台 reference(按目录跳转,不全读)⑤ 合并无依赖的 bash 调用:用 ; 串联进一条命令,减少工具往返(每次 ~1-2s)。
| 步骤 | 脚本/命令 | 成功信号(exit 0 且) | 失败(exit 1;2=参数错) |
|---|---|---|---|
| 前置 | api.mjs GET /v1/gallery/announcements/current --prefix open-api-guest | stdout 首行 #status=200 | 超时/拒连 → 提示排查网络 |
| 前置 | node <skill>/scripts/check-version.mjs(自动读自身 version + 请求远端版本,内部解析,stdout 仅状态行) | exit 0 且首行 status=ok/status=skip | exit 1(首行 status=outdated + 平台下发升级文案)→ 原样提示用户升级,停止发布 |
| 5/7 | api.mjs GET "/v1/gallery/camps" --creds-file <json> --fields "id,name,school,status,startsAt,endsAt" | #status=200(--fields 投影减 token) | 401 → 刷新 STS |
| 1 | scan-projects.mjs | stdout #project <n> <path> <type> | exit 1=无候选 |
| 1 | resolve-domain.mjs [--hcloud <exe>] | stdout #domain=<id> name=<name> | exit 1=hcloud 失败(stderr 含排查指引) |
| 1 | gen_sts.py --account <id> | 写 sts-creds.json(含 _refresh)+sts-creds.sh | 见 [troubleshooting#domain-id-resolution-issues] |
| 3 | strip-git-credential.mjs "<rawUrl>" | stdout=安全 URL(https:// 开头、.git 结尾、无 @) | @/非 https/非 .git → 停止发布 |
| 3 | ensure-gitcode-credential.mjs | #credential=found 或 #credential=missing(exit 0) | exit 1=Windows 无凭证且 skill 未装(stderr 含安装命令) |
| 3 | extract-workname.mjs <workDir> | stdout #name=<name> source=<来源> | exit 1=workDir 不存在 |
| 4 | preflight.sh / preflight.ps1 | 写 font-gate-ok(ok=true gate=preflight) | 截图/封面/图表禁止 |
| 4 | ensure-utf8.mjs <dir> | 写 utf8-gate-ok | 编码非 UTF-8 → 禁止截图 |
| 4 | build-cover.mjs --url … --project-dir … --title … --out … | stdout 一行 #cover=… layout=… multi=… | exit 1 → 截图/校验/合成任一失败,stderr 详情 |
| 4 | screenshot_guard.py http://… --out <png> --quiet | 写 screenshot-gate-ok + 出 PNG | 字形/字符集不过 → 修字体重试 |
| 4 | generate_cover.py --screenshot … --title … --out … | 封面 PNG,再过 verify-glyphs.py | exit 1 → 禁止继续合成 |
| 4/6 | verify-glyphs.py <img> --quiet | exit 0(stdout 仅 1 行省 token) | exit 1 → 疑似豆腐块 → 修字体 |
| 5 | count-cjk.mjs --file <utf8.txt> | 总字符数 15~50 | exit 1 → 改写简介再计 |
| 6 | generate_diagram.py --html … --out … | 图表 PNG,再过 verify-glyphs.py --quiet | 同封面 |
| 6 | build-detail-zip.mjs --readme <md> --resources <dir> --out <zip> | 打包+校验一步完成 | exit 1 → 按输出修复重打 |
| 8 | check-gates.mjs --cover <png> --detail <zip> --strict --max-age 3600 --quiet | exit 0(stdout 仅 1 行省 token) | exit 1 → 回退重跑门禁 |
| 8 | publish-work.mjs <utf8-params.json> | #status=201(仅输出 workId/workUrl/reward) | 409/其他;失败时原样输出完整响应 |
标记文件位置:Linux
/tmp/;Windowsos.tmpdir()(%TEMP%)。
yum install zip;Windows 用 .NET ZipFile)。Windows 首次见 windows-setup.md。api.mjs GET "/v1/gallery/announcements/current" --prefix open-api-guest,连通 OK 即可继续。node <skill>/scripts/check-version.mjs——脚本自动读本文件 frontmatter version: 并请求平台版本接口比较。status=ok/status=skip(非 2xx/解析失败)→ 继续,不阻塞;status=outdated → 将脚本输出的升级文案原样告知用户并停止发布(连同行公告接口一起两条命令可并行/串联合并执行,减少工具往返)。SELF_VERIFY 生成 STS 临时凭证(900s),永久 AK/SK 留本地不上传。node <skill>/scripts/scan-projects.mjs——自动递归 **/README.md,检查指示文件,排除 skills//node_modules//dist/ 等,扫描 CodeArts 沙箱目录。stdout 每行 #project <n> <path> <type>。workDir。node <skill>/scripts/resolve-domain.mjs [--hcloud <exe>]——自动 hcloud configure set --region + IAM KeystoneListAuthDomains + 提取 domain_id。stdout #domain=<id> name=<name>。hcloud 不在 PATH 时传 --hcloud。凭证优先级:env → hcloud 已配置 → 引导用户(troubleshooting#2)。python <skill>/scripts/gen_sts.py --account <domainID> → sts-creds.json(含 _refresh,使 401 自动刷新)。已获取 IAM Domain ID: <id>,用于将作品关联至您的华为云账号。Output: workDir、domainID、credsFile。
询问是否运行项目+开隧道。拒绝则 envUrl="" → Step 3。
netstat -ano | findstr :<port>(Win),残留进程 kill。npm run dev/python main.py);纯前端 npx http-server . -p <port>。≤30s 健康检查。curl -sf --max-time 3 http://localhost:<port> 首 100 字符须匹配本项目(<title>/项目名),Index of //404 → 清进程重启。devbridge host -p <port> -e 2,curl -sf --max-time 10 验证。失败不阻塞,置 envUrl=""。详见 devbridge-tunnel.md。Output: port、envUrl(或 "")。
gitUrl 必填,gitBranch 必须显式读取。workName 从 README 解析/合成(<30 字符)。
git -C <workDir> remote get-url origin + branch --show-current。node <skill>/scripts/ensure-gitcode-credential.mjs——自动检测本机 GitCode 凭证(git credential fill/~/.git-credentials/$GITCODE_TOKEN/cmdkey),无凭证时按平台给可行路径(Linux 提示手动配置 / Windows 检测 gitcode-oauth skill 并输出安装命令)。exit 0=有凭证或已有路径;exit 1=Windows 无凭证且 skill 未装(stderr 含安装命令 + Windows Git Bash 兼容提示)。gitUrl="$(node <skill>/scripts/strip-git-credential.mjs "$(git remote get-url origin)")"。硬校验:无 @、https:// 开头、.git 结尾。node <skill>/scripts/extract-workname.mjs <workDir>——按优先级 frontmatter → H1 → manifest → index.html <title> → 目录名提取。stdout #name=<name> source=<来源>。source=dirname 时 agent 可合成/修改(<30 字符)。Output: gitUrl、gitBranch、workName。
门禁(并行):
| 门禁 | 命令 | 标记 |
|---|---|---|
| 渲染 | preflight.sh(Win: preflight.ps1) | font-gate-ok |
| 编码 | ensure-utf8.mjs <dir> | utf8-gate-ok |
门禁失败禁止截图/合成。preflight 以标记文件 ok=true 为准(轮询 ≤180s)。Linux 首次运行必须后台化(下载 Chromium ~111MB,前台会被 shell 超时 SIGPIPE 杀死,主进程死后不会写 gate 文件,轮询徒劳);Windows 通常已预装系统浏览器,前台即可。
封面一键生成(内置:页数判定 + 单/多图概率抽样 + 截图 + 合成 + 校验):
node <skill>/scripts/build-cover.mjs \
--url "http://127.0.0.1:<port>" --project-dir <workDir> \
--title "<workName>" --tagline "<一句话>" --description "<主体介绍,1~3 句>" \
--out <coverImagePath>
.html 数 ≥2)额外加入 3 个多图布局(大图+小图拼贴),统一概率池等概率抽样,多页时多图出现概率 3/10。#cover=… layout=… multi=yes|no pages=<n> scheme=…;日志走 stderr。失败 exit 1。⚠️ workName↔封面强依赖: 封面标题即
--title "${workName}"。一旦之后因 409 改名,必须用新 workName 重新合成封面(见 Step 8 改名重推流程),禁止旧封面配新名发布。
排布规则、视觉基线见 work-preparation.md#screenshot-layout-rules。
Output: coverImagePath。
读写 README 生成一句话简介。总字符数 15~50,脚本计数通过才继续:
node <skill>/scripts/count-cjk.mjs --file "$env:TEMP\intro.txt" # Win 必须 --file(GBK 转码问题)
规则见 work-preparation.md#introduction-generation。
⏩ 并行:
api.mjs GET "/v1/gallery/camps" --query "pageNo=1&pageSize=100&periodYear=${currentYear}" --creds-file "${credsFile}" --fields "id,name,school,status,startsAt,endsAt"预取供 Step 7。
Output: introduction。
生成详解文章(500–1500 字)配图并打包 zip。
python <skill>/scripts/generate_diagram.py --html diagram.html --out resources/<name>.png --width 1280
python3 <skill>/scripts/verify-glyphs.py <img> --quiet # 每张含中文图必跑
build-detail-zip.mjs 内置正确目录结构 README.md@根 + resources/,打包后自动跑服务端同源校验,跨平台无需 zip/ZipFile):
node <skill>/scripts/build-detail-zip.mjs --readme <README.md> --resources <dir> --out <workDetail.zip>
Output: detailsZipPath。
startsAt/endsAt/status 判定投稿状态(api-spec.md#submission-status-determination)。school(学校)列;school 为空时显示 —。选不可投稿项提示「未开放投稿」。Output: trainingCampId。
开始发布作品 ${workName}。node <skill>/scripts/check-gates.mjs --cover <png> --detail <zip> --strict --max-age 3600 --quiet(exit 0 才继续;publish-work.mjs 内置 verifyGates() 也会拦截)。gallery-publish-${domainID}-${timestamp}:
node <skill>/scripts/publish-work.mjs <utf8-params.json> --max-age 3600
# 成功仅输出 workId/workName/workUrl/reward;失败原样输出完整响应
workId/workUrl/reward,清理临时文件。积分提示(无论是否成功都必须提示「每日仅限一次」):
is_success===true:✅ 积分领取请求已提交。⚠️ 每日仅限一次。前往查看余额:https://developer.huaweicloud.com/growis_success===false:积分领取未成功:<error_msg> + 每日限一次提示 + 成长中心地址 + 反馈入口 https://gallery.developer.huaweicloud.com/gallery/profile?tab=feedback。workName'(<30 字符):用户自定,或在不带版本的常规名基础上加序号(如 扫雷→扫雷-2)。禁止随意拼接无关字符。--title "<workName'>" 重新合成:
python <skill>/scripts/generate_cover.py --screenshot <原截图png> --title "<workName'>" --tagline "<原tagline>" --description "<原description>" --out <coverImagePath-新> --scheme auto
python3 <skill>/scripts/verify-glyphs.py <coverImagePath-新> --quiet # exit 0 才可继续
node <skill>/scripts/check-gates.mjs --cover <封面-新> --detail <zip> --strict --max-age 3600 --quiet。workName' + 新封面、新 Idempotency-Key 重新发布(回到第 3 步)。
注意:详情 zip 通常无需重做;仅当 zip 内 README 的标题或正文写了旧作品名(与新名不一致)时,才需要把旧名改为新名并重新打包详情 zip。<msg>(<code>),保留临时文件,问是否修改重发。错误码见 error-codes.md。| Document | 用途 | 何时读 |
|---|---|---|
| troubleshooting.md | 网络/各步骤失败排查 | 脚本报错→先查这里 |
| error-codes.md | 平台 API 全错误码表 | API 返回非 2xx 时查表 |
| api-spec.md | API 契约、投稿状态判定 | Step 7/8 解析响应 |
| work-preparation.md | 命名/封面/简介/详情/图表统一规则 | Step 3–6 需细节时按目录跳转 |
| work-preparation-windows.md | Windows 专属:PowerShell 预检/字体/zip | 仅 Windows |
| work-preparation-linux.md | Linux 专属:bash 预检/yum/fontconfig | 仅 Linux |
| windows-setup.md | Windows 首次准备:编码/Playwright/zip | Windows 环境首次 |
| self-verify-publish.md | 自委托 SELF_VERIFY 与 STS 原理 | Step 1 出问题时 |
| devbridge-tunnel.md | DevBridge 隧道登录/故障 | Step 2 隧道异常 |
| codearts-sandbox.md | CodeArts 沙箱目录 | Step 1 命中沙箱 |