media-upload — 媒体上传(图片/视频),输出 raw.githubusercontent.com URL
把媒体文件(截图、动图、录屏——单个或整目录)上传到公共图床仓(默认 ArcBlock/loop-agent-assets),
返回可匿名访问的 raw.githubusercontent.com/.../main/... URL。
Output language: 中文;代码 / 路径 / 命令保持原文。
⚠️ 上传统一,内嵌不统一(最容易踩的坑)
上传对 image/video 是同一套;但在 GitHub markdown 里怎么内嵌,按类型分流:
| 类型 | 扩展名 | 内嵌方式 |
|---|---|---|
| 图片 | png / jpg |  内联显示(#1334) |
| 动图 | gif |  内联播放(gif 是 image/gif,同图片路径) |
| 视频 | webm / mp4 / mov | 链接 [▶ 录屏](<raw-url>),或尽力 <video src=...>;绝不 ——GitHub 不会把 raw 视频渲染成播放器,![]() 只会破图 |
判据只看扩展名:.png/.jpg/.gif → ![]();.webm/.mp4/.mov → 链接。
机制 = 插件里两个通用脚本(任何仓库同样跑,自动从 git remote 探 source repo,落在 <slug>/<ctx>/)
先解析插件根,脚本都在其 scripts/ 下:
PLUGIN="${AGENTLOOP_ROOT:-$HOME/.claude/plugins/marketplaces/arcblock-agent-skills/plugins/agentloop}"
- 单个文件:
bash "$PLUGIN/scripts/gh-upload-media.sh" <media> [name]→ 自动选写通道(gh contents API / git push)→ content-type=image/*|video/*闸 → stdout 打印 raw.githubusercontent/main URL。 - 整目录:
SCREENSHOT_DIR=<dir> CONTEXT=<ctx> bash "$PLUGIN/scripts/gh-upload-dir.sh"→ 遍历目录(png/jpg/jpeg/gif/webm/mp4/mov)逐个调上面那个 → stdout 每行filename\turl;任一失败追加一行UPLOAD_FAILED\t<原因>(始终 exit 0,失败看这行不看退出码)。
Usage
/agentloop:media-upload <dir> # 上传目录下所有媒体 → filename\turl map
/agentloop:media-upload <file> # 单个(图片或视频)
# 消费方 skill 的 bash 里直接调脚本(自动化调机制、不调 slash 命令):
SCREENSHOT_DIR="$SHOT_DIR" CONTEXT="pr${PR}" bash "$PLUGIN/scripts/gh-upload-dir.sh" > url-map.tsv
拿到 map 后按上表分流内嵌。收到 UPLOAD_FAILED 行时在报告顶部显示 > ⚠️ 媒体上传失败 — <原因>
并降级 SendUserFile 直投(不能内嵌,但不产出损坏文件)。
上传器两通道都不可用时:浏览器原生附件上传(第三层兜底,issue #3010)
SendUserFile 只把文件发给当前对话的人,不会让证据落地到 GitHub——PR/issue 上依旧没有可核验的
截图。这类 review 门控(ui-verify 的 UI 证据闸最典型)需要证据持久发布在 GitHub 上,gh 与图床
仓 clone 又都不可用时,唯一确定性可重复的兜底是驱动一个已登录 GitHub 的浏览器走原生附件上传
(本轮真实产出过 arc#2991 / arcblock-site#165 / Site PR #166 的截图证据):
- 打开目标 PR/issue 的评论框,触发 file chooser(拖拽或点击 attach 按钮),选中本地媒体文件。
- GitHub 把文件传到自己的附件 CDN,返回
https://github.com/user-attachments/assets/<uuid>(或旧版user-images.githubusercontent.com/...)URL 并自动插入引用到评论正文。 - 提交前用浏览器截图/读 DOM 确认媒体已渲染成可见图片/视频,不是破链接——不能假设上传成功。
- 同时贴到 PR 和它关联的 issue(
Fixes #<n>/Part of #<n>),不要只贴一处。
这条 URL 家族(github.com/user-attachments/assets/…、user-images.githubusercontent.com/…)与
raw.githubusercontent.com/<repo>/main/… 一样被下游门控(如 .claude/skills/ui-verify/scripts/gate-comment.ts
的 isPublishedUrl())当作已发布证据;本地文件路径、SendUserFile 附件、或任何未验证可匿名读的
URL 都不算——上传/发布失败绝不能被下游报告描述成"已完成验证",该标注 BLOCKED/"证据缺失"就
标注,不用中性措辞掩盖(术语与 ui-verify/pr-review/pr-sweep/issue-review 保持一致)。
此兜底需要已认证的 GitHub 浏览器会话,与 gh-upload-media.sh 的两条自动化通道不同层级——不总是
每个环境都可行;不可行时按上一节降级 SendUserFile,消费方门控按"证据缺失"处理,不得放行。
为什么必须是 raw.githubusercontent.com/main(脚本已强制,别自己换)
- #1334:GitHub MCP 写侧(
add_issue_comment/issue_write)的 sanitizer 把剥成纯链接—— 唯一例外是 host=raw.githubusercontent.com且 ref=main。绝不用cdn.jsdelivr.net、绝不用 session 分支 ref。 (gh pr comment/gh issue comment发的评论不受此限,但统一走 raw.githubusercontent/main 对两条发帖通道都安全。) - #1079:MCP 文件写工具(
create_or_update_file/push_files)对二进制二次 base64 编码 → 存成 ASCII 字符串不是媒体。媒体二进制永远不走任何 MCP 写工具——脚本走 gh contents API 或 git push, 并对返回 URL 做 content-type=image/*|video/*抽查(非二者 = 双编码/损坏,判失败)。
双通道(脚本自动选,调用方无需关心)
- 通道 A —
ghcontents API:gh存在且对图床仓有写权限时用(本地 PAT / 有写权限的 token);带瞬时 5xx 重试。 - 通道 B — git push:无
gh(或无写权限)时,cp进已 clone 的图床仓工作区($ASSETS_REPO_DIR或探测~/loop-agent-assets等)→commit+push origin main(带 fetch+rebase 重试)。cloud routine 里 gh token 是 placeholder(对本 org 写接口 403,#1334),走的就是这条——前提是图床仓配为第二个 git source。
两通道都不可用 → 脚本 exit 2 → 消费方降级 SendUserFile。
消费方
test-sweep(arc)、ui-verify(arcblock-site,及将来 did)等在自己的 bash 里调gh-upload-dir.sh。- 机制永远是那两个脚本,别再各仓手撸
for f in *.png循环(arc 与 arcblock-site 曾各写一份、已漂移——#1037)。 需要人读的 doctrine + 可发现入口就是本 skill。