The learning station, lecture HTML, and catalogs ship as one static tree. After a merge to main, GitHub Actions builds _site/ and publishes it to Cloudflare Pages (the Git repository may stay private). Later edits to a lecture or a lab land on the same URL. You do not need ports 7200 / 7300 / 7400 on the public site.
Recommended URL (Cloudflare; a private repo can still serve a public site):
https://kaiyuan-xiangmu-jiexi-zixi.pages.dev/
English catalog: https://kaiyuan-xiangmu-jiexi-zixi.pages.dev/en/
(If the first deploy finds the name taken, Cloudflare appends a suffix. Trust the URL in the Actions log.)
Fallback: GitHub Pages https://mikayiyang.github.io/kaiyuan-xiangmu-jiexi-zixi/ (the free plan requires a public repository).
The site root redirects to the Chinese catalog. Interactive stations live on the same origin:
| Local port | Public path |
|---|---|
:7300 Claude Code v1 |
/learn/claude-v1/ |
:7301 Claude Code v2 |
/learn/claude-v2/ |
:7200 OpenManus × Suna |
/learn/manus/ |
:7200/build-guide.html |
/learn/manus/build-guide.html |
:7397 Build-an-agent guide |
/learn/agent-guide/ |
:7380 DeepSeek Harness (if that station is merged) |
/learn/dsh/ |
:7400/….html lectures (including Easel) |
same filename at the site root |
| English lectures | /en/ |
Source catalogs still write 127.0.0.1 ports so local multi-process preview keeps working. Only the build product _site/ rewrites those URLs to relative paths. Do not hand-edit the AUTO-CATALOG region in the Chinese catalog.
The build runs in GitHub Actions. By default it force-pushes _site/ to the site branch. Cloudflare should host that branch; it should not compile from main (that environment is a poor fit for Python plus four Vite apps).
Which hosting option
| Option | Domain | Can the repo stay private? | Do you need a server? |
|---|---|---|---|
A. Cloudflare Pages on the site branch (recommended) |
*.pages.dev |
Yes; the site itself is public | No |
| A2. Wrangler direct upload | same | Yes | No; the token must be allowed to manage Pages |
| B. GitHub Pages | mikayiyang.github.io/kaiyuan-xiangmu-jiexi-zixi/ |
Free plan needs Public | No |
| C. Your own small VPS | IP or your domain | Yes | Yes; rsync _site/ |
github.io does not point at your machine and does not point at Cloudflare. They are independent.
A. Cloudflare Pages via Git (stable; no API token required)
Wrangler direct upload has returned error 7003 even when the Account ID length was correct and the secret reached Actions. That usually means the token lacks Pages permission, or the token and the account are not the same Cloudflare account—not a mistyped ID.
So the pipeline is: on merge to main, push the static tree to the site branch. You connect Git in Cloudflare once.
1. Merge the PR so Actions can push site
When Actions → Deploy site is green, the repository should show branch site:
https://github.com/mikayiyang/kaiyuan-xiangmu-jiexi-zixi/tree/site
2. Attach that branch in Cloudflare
- Open Workers & Pages.
- In the 2026 dashboard, Create opens Workers, not Pages. Use the small link Looking to deploy Pages? Get started, or open Create Pages directly. Do not click the large Connect-to-GitHub box on the Workers screen.
- Import an existing Git repository. Authorize GitHub. Select
kaiyuan-xiangmu-jiexi-zixi(private is fine). - Settings:
- Production branch:
site(notmain) - Framework preset: None
- Build command: empty
- Build output directory:
/ - Save / Deploy.
After that, every merge to main overwrites site, and Cloudflare publishes. The URL remains *.pages.dev.
The red “Cloudflare Pages — Building” check on a pull request
If GitHub build (Actions · Deploy site) is green, you can merge. The Cloudflare red X is a Git preview deploy: Cloudflare checks out the PR’s source branch, looks for _site/ (gitignored, not on that branch), and reports Build failed. That is not “the English station failed to build,” and it does not block publishing.
What actually ships: merge to main → Actions pushes the site branch → Cloudflare hosts site.
To make the red X go away (optional):
- Cloudflare → this Pages project → Settings → Builds & deployments
- Turn Preview deployments off, or include only branch
site - Keep production branch
site, empty build command, output/
wrangler.toml in this repo no longer sets pages_build_output_dir = "_site", so Cloudflare will not keep looking for that folder on PRs.
A2. Wrangler direct upload (optional)
If the token’s permissions are correct, the same workflow still attempts wrangler pages deploy. Failure does not block the site branch.
Create the token from the Edit Cloudflare Workers template (it includes Pages). Do not grant only one custom line:
- Account API tokens → Create Token → Edit Cloudflare Workers → Use template.
- Account Resources: the same account.
- Copy the Account ID from the Workers & Pages overview (right-hand side). Do not copy a Zone ID under a domain.
- Repository Repository secrets:
CLOUDFLARE_API_TOKENCLOUDFLARE_ACCOUNT_ID
Local one-shot upload (optional)
export CLOUDFLARE_API_TOKEN='…'
export CLOUDFLARE_ACCOUNT_ID='…'
./scripts/deploy-cloudflare.sh
B. GitHub Pages (optional fallback; the current workflow does not push it)
This repository is private. GitHub’s free plan does not serve public Pages from a private repo. Actions currently pushes the site branch for Cloudflare, not a gh-pages branch for github.io.
If the repo later becomes Public and you want github.io:
- GitHub repo → Settings → General → Danger Zone → Change repository visibility → Public.
- Run
python3 scripts/build-site.py, then place_site/on the branch / official Pages workflow GitHub requires. - Settings → Pages as in GitHub’s docs.
A private repo with GitHub Pro / Team can serve Pages only to people who can read the repo. Day-to-day study should use pages.dev above.
C. Mirror the same tree on a small VPS
Useful if you already have a public IP or a domain. Independent of Cloudflare.
python3 scripts/build-site.py
rsync -a --delete _site/ user@your-server:/var/www/learn/
Nginx example:
server {
listen 80;
server_name _;
root /var/www/learn;
index 总目录.html index.html;
location / {
try_files $uri $uri/ /总目录.html;
}
}
Chinese filenames need a UTF-8 locale on the server.
Preview the same tree locally
python3 scripts/build-site.py # emit _site/
python3 -m http.server 7400 --directory _site --bind 127.0.0.1
# open ../总目录.html
# English: ./
./start-html-previews.sh assembles _site/ by default and points 7400 at that unified tree. Skip assembly with UNIFIED=0 ./start-html-previews.sh.
How later edits reach the public site
Edit Markdown / a learning station as usual → python3 build-html.py (if you touched a Markdown file that script indexes) → python3 scripts/build-en-html.py (if you touched English lectures) → python3 update-catalog.py → commit and merge to main. Actions rebuilds _site and publishes. You do not need 7200 / 7300 in order to read the public site.