skills.idiotfan.wang 是我给 Claude Code / Codex CLI / DSH 准备的技能包分发站,目前挂着 21 个 skill。这篇文章记一下它是怎么搭的——不复杂,但有几个值得说的小细节。
为什么会有这个站
我有一堆 skill,散落在 Gitea 的私有仓库里:大梦工资计算、腾讯文档、微信读书、GitNexus、各种图像/音乐生成…… 目录结构是标准的 Anthropic skill 格式——每个技能一个目录,里面一个 SKILL.md(front matter 里写 name / description / version),大一点的再带 scripts/、references/。
仓库自己用没问题,但有几个痛点:
- 换台机器就要重新 clone 私有仓库,还要配认证,麻烦
- 想给朋友用,总不能把 Gitea 账号密码给他
- 技能更新了,别人拿到的还是旧版
所以我要的其实很简单:一个看得见、能下载、能校验的静态站——首页列出所有技能,点一下就能下载打包好的 tar.gz,附 sha256 校验和。
站点长什么样
纯手写静态页,零框架,就一个 index.html 内嵌 CSS + 一小段原生 JS:
- GitHub 暗色风格,卡片网格展示技能(emoji 图标、名称、版本徽标、两行描述、体积、适用目标)
- 顶部搜索框 + 分类筛选:全部 / 3D / 办公 / 图像 / 大梦 / 媒体 / 开发 / 系统 / 网络
- 统计条:
21 个技能 · 384 KB 打包体积 · 更新于 xx UTC
数据全部由一个 Python 脚本生成,没有任何运行时后端。
同步机制:cron 轮询,不用 webhook
一开始我也考虑过 Gitea Actions 或者 webhook,但最后选了最简单可靠的方式——VPS 上挂 cron:
*/15 * * * * /opt/skill-server/rebuild.sh >> /var/log/skill-rebuild.log 2>&1
rebuild.sh 干两件事:拉代码、跑构建:
| |
几个设计点:
- Token 存在
/root/.gitea-skill-token文件里,脚本里不写死密码,换 token 只改一个文件 git pull失败也继续跑(|| true),旧版本照常发布,不会把站搞挂- 15 分钟一次的轮询对个人站完全够用,还省掉了 webhook 的运维负担
build.py:一站式的构建脚本
构建脚本在 /opt/skill-server/build.py(12KB,无第三方依赖,只用标准库)。它依次做四件事:
1. 解析 SKILL.md front matter
每个技能目录读 SKILL.md 的头部 YAML,提取 name / version / description / whenToUse:
| |
版本号直接从仓库里的 SKILL.md 来,所以升级技能 = 改 front matter 里的 version 再 push,站上自动出现新版本。
2. 打包 + 同步
- 每个技能目录打成
{name}-{version}.tar.gz,丢到/var/www/skills/bundles/ - 原始目录整个复制到
/var/www/skills/skills/{name}/,nginx 开 autoindex,目录列表直接当详情页用,连详情页都不用写
3. 生成 index.json
每个技能记录 name / version / description / whenToUse / url / sha256 / size / targets,其中 sha256 是打包文件的校验和——下载后可以核对完整性,这是"一键安装"的信任基础。
4. 生成 index.html
Python 里直接拼 HTML 字符串(内嵌 CSS),加上 emoji 图标映射表和分类映射表:
| |
标题里的"更新于"时间戳用 datetime.now(timezone.utc) 生成,所以你能在站上看到 UTC 时间。
容易被忽略的安全细节
技能包里有不少敏感内容:API key、cookies、访问凭证。发布前做了两道保险:
| |
EXCLUDE_FILES:打包和同步时直接跳过敏感文件REDACT:对.py/.sh/.js/.md做正则脱敏,把硬编码的密钥替换成空串和提示
提醒:代码里的密钥永远是隐患。脱敏是兜底,最好还是在源仓库里就别提交密钥。
访问控制:nginx basic auth
站点整套用 nginx 服务,外面套了一层 Basic Auth:
| |
访问时浏览器会弹认证框,realm 就是"idiotfan skills (需认证)"。自己用或者给朋友发个账号都行,不用把 Gitea 权限开出去。
顺带一提:博客也是同一个套路
这套"cron 轮询 + git pull + 构建脚本"的思路我博客也在用:
*/5 * * * * /opt/blog-server/build.sh >> /var/log/blog-build.log 2>&1
| |
所以博客"写完 push、几分钟内自动上线"就是这么来的——没有 webhook、没有 Actions runner,就一条 cron。对个人自建站来说,越简单的机制越不容易坏。
小结
整个站就三个组成部分:
| 组件 | 作用 |
|---|---|
| Gitea 私有仓库 | 技能源码的唯一事实来源 |
/opt/skill-server/(cron + 脚本) | 拉取、脱敏、打包、生成页面 |
| nginx + /var/www/skills | 静态服务 + 认证 + autoindex |
下一步可能给站上加"更新日志"或者按安装目标(claude/codex/dsh)过滤,不过现在的版本已经够用了。自建服务的乐趣大概就在这:每个零件都看得见摸得着,坏了也修得快。