Publish the work to the Huawei Cloud University Operations Platform.For deterministic subtasks, call the built-in script first(single entry, fail-fast, built-in --help), stdout/exit code is the basis for judgment. Only when the script reports an error, check references/troubleshooting.md。
Pipeline
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) ← 依赖以上全部
Parallel (after Step 2 completes):Step 3/4/5/6 Independent of each other, all four run in parallel(take the longest path Step 4 ~90s, instead of serial ~160s). Within Step 4 preflight + ensure-utf8 then parallel.
Variables run throughout:port/workName/envUrl Once determined at the earliest step, it is passed as a variable through the whole flow; hardcoding is prohibited (the tunnel URL contains port, and when the port changes, envUrl changes accordingly).
Execution model:① Script contract first——judge by stdout success signal,zero source code reading on the success path; only on failure 脚本提示 → troubleshooting 对应节 → grep 脚本源码 check level by level ② Do not manually bypass the gate (publish-work.mjs built-in verifyGates() re-check) ③ Network timeout is uniformly 3s ④ detect-env.mjs After determining the platform, only read the reference for the corresponding platform (navigate by directory, do not read everything) ⑤ Merge bash calls with no dependencies: use ; to chain into one command, reducing tool round trips (~1-2s each).
Script contract quick reference
| Step | Script/Command | Success signal (exit 0 and) | Failure (exit 1; 2=parameter error) |
|---|---|---|---|
| Precondition | api.mjs GET /v1/gallery/announcements/current --prefix open-api-guest |
stdout first line #status=200 |
Timeout/connection refused → prompt to troubleshoot the network |
| Precondition | node <skill>/scripts/check-version.mjs(automatically reads its own version + requests the remote version, parses internally, stdout only status line) |
exit 0 and first line status=ok/status=skip |
exit 1 (first line status=outdated + upgrade text issued by the platform) → prompt the user to upgrade as-is, stop publishing |
| 5/7 | api.mjs GET "/v1/gallery/camps" --creds-file <json> --fields "id,name,school,status,startsAt,endsAt" |
#status=200(--fields projection reduces tokens) |
401 → refresh STS |
| 1 | scan-projects.mjs |
stdout #project <n> <path> <type> |
exit 1=no candidate |
| 1 | resolve-domain.mjs [--hcloud <exe>] |
stdout #domain=<id> name=<name> |
exit 1=hcloud failure (stderr contains troubleshooting guidance) |
| 1 | gen_sts.py --account <id> |
writes sts-creds.json(contains _refresh)+sts-creds.sh |
see [troubleshooting#domain-id-resolution-issues] |
| 3 | strip-git-credential.mjs "<rawUrl>" |
stdout=safe URL (starts with https://, ends with .git, no @) |
@/not https/not .git → stop publishing |
| 3 | ensure-gitcode-credential.mjs |
#credential=found or #credential=missing(exit 0) |
exit 1=Windows has no credentials and skill is not installed (stderr contains install command) |
| 3 | extract-workname.mjs <workDir> |
stdout #name=<name> source=<来源> |
exit 1=workDir does not exist |
| 4 | preflight.sh / preflight.ps1 |
writes font-gate-ok(ok=true gate=preflight) |
screenshot/cover/chartprohibited |
| 4 | ensure-utf8.mjs <dir> |
writes utf8-gate-ok |
encoding not UTF-8 → screenshots prohibited |
| 4 | build-cover.mjs --url … --project-dir … --title … --out … |
stdout one line #cover=… layout=… multi=… |
exit 1 → any of screenshot/validation/composition failed, stderr details |
| 4 | screenshot_guard.py http://… --out <png> --quiet |
writes screenshot-gate-ok + outputs PNG |
glyph/charset not passing → fix font and retry |
| 4 | generate_cover.py --screenshot … --title … --out … |
cover PNG, then pass verify-glyphs.py |
exit 1 → proceed to composition prohibited |
| 4/6 | verify-glyphs.py <img> --quiet |
exit 0 (stdout only 1 line to save tokens) | exit 1 → suspected tofu blocks → fix font |
| 5 | count-cjk.mjs --file <utf8.txt> |
Total character count 15~50 | exit 1 → Rewrite the introduction and recount |
| 6 | generate_diagram.py --html … --out … |
Chart PNG, then pass verify-glyphs.py --quiet |
same as cover |
| 6 | build-detail-zip.mjs --readme <md> --resources <dir> --out <zip> |
Package + verify in one step | exit 1 → Fix and repackage according to the output |
| 8 | check-gates.mjs --cover <png> --detail <zip> --strict --max-age 3600 --quiet |
exit 0 (stdout only 1 line to save tokens) | exit 1 → Roll back and rerun the gate |
| 8 | publish-work.mjs <utf8-params.json> |
#status=201(only output workId/workUrl/reward) |
409/other; on failure, output the complete response as-is |
Marker file location: Linux
/tmp/;Windowsos.tmpdir()(%TEMP%)。
Pre-lock
- hcloud + AK/SK: Required for Step 1. Not installed/not configured → troubleshooting#domain-id-resolution-issues。
- git:Step 3;zip:Step 6(Linux
yum install zip; on Windows use .NETZipFile). On Windows, first-time see windows-setup.md。 - Connectivity:
api.mjs GET "/v1/gallery/announcements/current" --prefix open-api-guest, if connectivity is OK, continue. - Version check (best effort):
node <skill>/scripts/check-version.mjs— the script automatically reads this file's frontmatterversion:and requests the platform version API for comparison.status=ok/status=skip(non-2xx/parse failure) → continue, do not block;status=outdated→ use the script's upgrade messageas-isinform the user andstop publishing(together with the platform announcement API, the two commands can be executed in parallel or serially combined to reduce tool round trips).
Core Workflow
Step 1: Select Work Directory & Resolve IAM Domain Info
- Inform the user(selecting a directory = consent): will create IAM self-delegation
SELF_VERIFYto generate STS temporary credentials (900s); permanent AK/SK stay local and are not uploaded. - Scan project root:
node <skill>/scripts/scan-projects.mjs— automatically recurse**/README.md, check for indicator files, excludeskills//node_modules//dist/etc., and scan the CodeArts sandbox directory. Each stdout line is#project <n> <path> <type>。 - List candidates, wait for user selection →
workDir。 - Parse Domain ID:
node <skill>/scripts/resolve-domain.mjs [--hcloud <exe>]— automaticallyhcloud configure set --region+IAM KeystoneListAuthDomains+ extractdomain_id。stdout#domain=<id> name=<name>. When hcloud is not in PATH, pass--hcloud. Credential priority: env → hcloud already configured → guide user (troubleshooting#2)。 - Generate STS:
python <skill>/scripts/gen_sts.py --account <domainID>→sts-creds.json(includes_refresh, so that 401 automatically refreshes). - Output:
已获取 IAM Domain ID: <id>,用于将作品关联至您的华为云账号。
Output: workDir、domainID、credsFile。
Step 2: Run Project & Open Tunnel
Ask whether to run the project + open a tunnel. If declined, envUrl="" → Step 3。
- Port cleanup:
netstat -ano | findstr :<port>(Win), kill leftover processes. - Startup: prefer the entry script (
npm run dev/python main.py); pure frontendnpx http-server . -p <port>. ≤30s health check. - Verify content:
curl -sf --max-time 3 http://localhost:<port>The first 100 characters must match this project (<title>/project name),Index of //404 → clear processes and restart. - Tunnel:
devbridge host -p <port> -e 2,curl -sf --max-time 10verification. Failure does not block; setenvUrl="". See devbridge-tunnel.md。
Output: port、envUrl(or "")。
Step 3: Git Repository Info & Work Name
gitUrl required,gitBranch must be read explicitly.workName Parse/synthesize from README (<30 characters).
git -C <workDir> remote get-url origin+branch --show-current。- Credential troubleshooting:
node <skill>/scripts/ensure-gitcode-credential.mjs— automatically detect local GitCode credentials (git credential fill/~/.git-credentials/$GITCODE_TOKEN/cmdkey). When there are no credentials, provide a feasible path by platform (on Linux, prompt manual configuration / on Windows, detect thegitcode-oauthskill and output the installation command). exit 0 = has credentials or an existing path; exit 1 = Windows has no credentials and the skill is not installed (stderr contains the installation command + Windows Git Bash compatibility hint). - Credential stripping:
gitUrl="$(node <skill>/scripts/strip-git-credential.mjs "$(git remote get-url origin)")". Hard validation: does not start with@、https://,.gitend. - Naming:
node <skill>/scripts/extract-workname.mjs <workDir>— by priority frontmatter → H1 → manifest →index.html <title>→ directory name extraction. stdout#name=<name> source=<来源>。source=dirnamewhen the agent can synthesize/modify (<30 characters).
Output: gitUrl、gitBranch、workName。
Step 4: Cover Image
Gate (parallel):
| Gate | Command | Marker |
|---|---|---|
| Render | preflight.sh(Win: preflight.ps1) |
font-gate-ok |
| Encode | ensure-utf8.mjs <dir> |
utf8-gate-ok |
Gate failure prohibits screenshots/synthesis. preflight is based on the marker file ok=true (polling ≤180s).Linux first run must be backgrounded(downloading Chromium ~111MB; in the foreground it will be killed by the shell timeout SIGPIPE, and after the main process dies it will not write the gate file, making polling futile); Windows usually has the system browser preinstalled, so foreground is fine.
One-click cover generation (built-in: page count determination + single/multiple image probability sampling + screenshot + synthesis + validation):
node <skill>/scripts/build-cover.mjs \
--url "http://127.0.0.1:<port>" --project-dir <workDir> \
--title "<workName>" --tagline "<一句话>" --description "<主体介绍,1~3 句>" \
--out <coverImagePath>
- Layout decisions are closed-loop inside the script(agent-unaware): single-page projects use only 7 single-image layouts; multi-page projects (
.htmlcount ≥2) additionally include 3 multi-image layouts (large image + small image collage), sampled uniformly with equal probability from a unified probability pool; for multi-page projects the probability of multi-image appearing is 3/10. - Multi-image layouts capture thefirst page + candidate pages(up to 3 small images); if candidate page screenshots fail they are automatically skipped, with a minimum guarantee of 1 image.
- stdout outputs only one line of result:
#cover=… layout=… multi=yes|no pages=<n> scheme=…; logs go to stderr. Failure exits 1.
⚠️ workName↔cover strong dependency: the cover title is
--title "${workName}"。Once renamed later due to 409, the cover must be re-synthesized with the new workName(see Step 8 rename and republish flow); publishing an old cover with a new name is prohibited.
For layout rules and visual baseline see work-preparation.md#screenshot-layout-rules。
Output: coverImagePath。
Step 5: Introduction
Read/write README to generate a one-sentence introduction.Total character count 15~50, continue only after the script count passes:
node <skill>/scripts/count-cjk.mjs --file "$env:TEMP\intro.txt" # Win 必须 --file(GBK 转码问题)
For rules see work-preparation.md#introduction-generation。
⏩ Parallel:
api.mjs GET "/v1/gallery/camps" --query "pageNo=1&pageSize=100&periodYear=${currentYear}" --creds-file "${credsFile}" --fields "id,name,school,status,startsAt,endsAt"prefetch for Step 7.
Output: introduction。
Step 6: Work Details
Generate a detailed article (500–1500 characters) with illustrations and package it into a zip.
- Diagrams: system architecture diagram required; optional 0–2 (routing/dependency/statistics diagrams). Use DevLens/GitNexus to understand the project before producing diagrams. Emoji are prohibited.
python <skill>/scripts/generate_diagram.py --html diagram.html --out resources/<name>.png --width 1280 python3 <skill>/scripts/verify-glyphs.py <img> --quiet # 每张含中文图必跑 - Packaging zip + validation in one step(
build-detail-zip.mjsbuilt-in correct directory structure README.md@root + resources/; after packaging, automatically run server-side same-origin validation, cross-platform without needing zip/ZipFile):node <skill>/scripts/build-detail-zip.mjs --readme <README.md> --resources <dir> --out <workDetail.zip>
Output: detailsZipPath。
Step 7: Select Training Camp
- Get the training camp list (if prefetched in Step 5, use it directly).
- Based on
startsAt/endsAt/statusdetermine submission status (api-spec.md#submission-status-determination)。 - displayed in two tables: "can submit" / "cannot submit",each row must include the
school(school) column;schoolwhen empty display—. Selecting a non-submittable item prompts "submission not open".
Output: trainingCampId。
Step 8: Publish
- Declare
开始发布作品 ${workName}。 - Gate recheck:
node <skill>/scripts/check-gates.mjs --cover <png> --detail <zip> --strict --max-age 3600 --quiet(continue only on exit 0;publish-work.mjsbuilt-inverifyGates()will also intercept). - Parameters written to UTF-8 JSON, Idempotency-Key=
gallery-publish-${domainID}-${timestamp}:node <skill>/scripts/publish-work.mjs <utf8-params.json> --max-age 3600 # 成功仅输出 workId/workName/workUrl/reward;失败原样输出完整响应 - 201 → success, take
workId/workUrl/reward, clean up temporary files.Points notice(regardless of success or failure, must prompt "only once per day"):is_success===true:✅ 积分领取请求已提交。⚠️ 每日仅限一次。前往查看余额:https://developer.huaweicloud.com/growis_success===false:积分领取未成功:<error_msg>+ daily once-only notice + growth center address + feedback entryhttps://gallery.developer.huaweicloud.com/gallery/profile?tab=feedback。
- 409 DUPLICATE → stop the current publish. Give options: ① abandon ② rename and republish.If renaming and republishing, must link:
- new name
workName'(<30 characters): user-defined, or add a sequence number to the regular name without a version (e.g.,扫雷→扫雷-2). Arbitrarily concatenating unrelated characters is prohibited. - Cover regeneration(the cover title must match the new name): use the original screenshot + original tagline/description, only replace
--title "<workName'>"and recomposite: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 才可继续 - Gate review (cover has been updated, must be rerun):
node <skill>/scripts/check-gates.mjs --cover <封面-新> --detail <zip> --strict --max-age 3600 --quiet。 - Usenew
workName'+ newcover and new Idempotency-Key to republish (return to Step 3). Note: the detail zip usually does not need to be redone; only when the title or body text of the README inside the zip containsold work name(inconsistent with the new name) do you need to change the old name to the new name and repackage the detail zip.
- new name
- Other errors → inform
<msg>(<code>), keep temporary files, and ask whether to modify and resend. For error codes, see error-codes.md。
References
| Document | Purpose | When to read |
|---|---|---|
| troubleshooting.md | Troubleshooting network/step failures | Script error → check here first |
| error-codes.md | Complete platform API error code table | Consult the table when the API returns non-2xx |
| api-spec.md | API contract, submission status determination | Step 7/8 parse response |
| work-preparation.md | Unified rules for naming/cover/description/details/charts | For details in Steps 3–6, jump by table of contents |
| work-preparation-windows.md | Windows-specific: PowerShell precheck/fonts/zip | Windows only |
| work-preparation-linux.md | Linux-specific: bash precheck/yum/fontconfig | Linux only |
| windows-setup.md | Windows first-time preparation: encoding/Playwright/zip | Windows environment first time |
| self-verify-publish.md | Self-delegation SELF_VERIFY and STS principles | When Step 1 has problems |
| devbridge-tunnel.md | DevBridge tunnel login/failure | Step 2 tunnel anomaly |
| codearts-sandbox.md | CodeArts sandbox directory | Step 1 hits the sandbox |