这是什么
cloud-artifacts 是一个独立部署的 HTML 托管服务:向
POST /upload 提交一段 HTML,服务把它写进 R2 bucket,并返回一个形如
/<7d|30d>/<id>.html 的公开 URL。GET 该 URL 即原样返回
HTML(含其中的 <script>)。文件到期由 R2 lifecycle rule 自动删除,
Worker 本身不参与过期处理。
它由三部分构成:Deno Deploy 边缘代理(生产出口,改善国内访问)、 Cloudflare Worker(鉴权 / 校验 / 读写 R2)、R2 bucket(按 prefix 分 TTL)。
在 CLI 侧,对应的能力是一个延迟工具 artifact(用户可见名
Artifact):读取本地文件并调用上述服务;支持 .html / .htm
原样上传,.md / .markdown 会先转成带样式的 HTML 再上传。
配套还有一个 /artifacts 斜杠命令与 /use-artifacts 内置技能。
与 packages/remote-control-server/ 定位相同:monorepo 的 workspaces
会自动识别本包,但主 CLI 不会 import 它。CLI 只是通过 HTTP 调用它的 API。
前置条件
-
使用公共托管服务:无需任何配置。CLI 侧的
artifact工具内置了默认 token 与默认服务地址。 -
自托管部署:需要一个 Cloudflare 账号,且本机已执行
npx wrangler login登录目标账号;需启用 R2。 -
Deno Deploy 代理层(可选):由部署者另配(
CNAME指向alias.deno.net,并在 Deno Deploy 项目里把上游设为https://<worker>.<account>.workers.dev)。不配也可直连 Worker。 -
自托管时的资源约定:
wrangler.toml中 R2 binding 名为BUCKET,bucket 名为cloud-artifacts,兼容日期2026-06-20。
安装与部署
在包目录里安装;在 monorepo 根执行亦可(workspace 自动识别)。
cd packages/cloud-artifacts
bun install
cp .dev.vars.example .dev.vars
# 编辑 .dev.vars,填入 TOKEN(仅 wrangler dev 读取)
bun run setup 执行 scripts/setup.sh:创建 R2 bucket、添加两条
lifecycle rule(7d/ 删 7 天、30d/ 删 30 天),并通过
wrangler secret put TOKEN 设置生产上传密钥。
bun run setup
在 Cloudflare Dashboard 为 Worker 绑定 Custom Domain(POST 与 GET 共用同一域名),
再把 wrangler.toml 的 [vars] PUBLIC_URL 改为该对外出口域名
(生产用 https://cloud-artifacts.qianshou-dashi.win)。
bun run deploy
本地开发:bun run dev(wrangler dev)启动本地 Miniflare +
本地 R2 模拟,默认端口 8787:
bun run dev
curl -X POST "http://localhost:8787/upload" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: text/html" \
--data-binary @/tmp/t.html
运行测试:scripts/test.sh 覆盖 7 个错误用例、3 个成功用例与 R2 写入验证,
支持直连(按 status code 断言)与经 Deno Deploy 代理(按 body 的 error
字段断言)双模式:
WORKER_URL=https://cloud-artifacts.qianshou-dashi.win \
TOKEN=<your-token> \
bash scripts/test.sh
配置
Worker 变量(wrangler.toml 的 [vars]):
| 变量 | 默认值 | 说明 |
|---|---|---|
PUBLIC_URL |
https://cloud-artifacts.qianshou-dashi.win |
对外出口域名,用于拼接返回的 url;部署到自有域名后必须修改 |
DEFAULT_TTL_DAYS |
7 |
未传 ?ttl= 时的默认 TTL(天) |
MAX_TTL_DAYS |
30 |
TTL 上限(天) |
MAX_BYTES |
10485760 |
上传体积上限(10MB) |
密钥与绑定:
| 项 | 说明 |
|---|---|
TOKEN(secret) |
上传侧唯一鉴权:Authorization: Bearer <TOKEN> 必须与之完全相等。生产用 wrangler secret put TOKEN 注入,即时生效、无需 redeploy |
BUCKET(R2 binding) |
绑定到 bucket cloud-artifacts,key 形如 <7d|30d>/<id>.html |
.dev.vars |
本地开发用的 TOKEN,仅 wrangler dev 读取 |
CLI 侧环境变量(覆盖内置默认值,用于自托管服务):
| 变量 | 默认值 | 说明 |
|---|---|---|
CLAUDE_ARTIFACTS_TOKEN |
qianshou-dashi |
上传用的 Bearer token,须与自托管服务的 TOKEN 一致 |
CLAUDE_ARTIFACTS_URL |
https://cloud-artifacts.qianshou-dashi.win |
服务基地址;getUploadUrl() 会去掉尾部斜杠后追加 /upload |
常用命令与参数
HTTP API:
| 方法与路径 | 说明 |
|---|---|
POST /upload |
Bearer 鉴权后校验 MIME、体积与 ttl,写 R2 并返回 {id, url, expiresAt} |
GET /<7d|30d>/<id>.html |
从 R2 读回,返回 text/html; charset=utf-8 与 Cache-Control: public, max-age=86400;prefix 非 7d/30d 返回 404 |
POST /upload 参数:
| Header / Query | 必填 | 说明 |
|---|---|---|
Authorization: Bearer <TOKEN> |
是 | 与 Worker secret TOKEN 完全相等 |
Content-Type: text/html |
是 | 不接受其他类型(以 text/html 开头为准) |
?ttl=7|30 |
否 | 默认 7,只允许 7 或 30 |
?hash=<custom-id> |
否 | 自定义 ID,须匹配 ^[A-Za-z0-9_-]{1,128}$;指定时覆盖同 ID 旧版本 |
| body | 是 | 原始 HTML(--data-binary @file.html),不超过 10MB |
错误码(直连 Worker 时的 HTTP status;经 Deno Deploy 代理会统一变成 200,按 body
的 error 字段判断):
| 状态码 | error code | 触发条件 |
|---|---|---|
| 400 | invalid_ttl |
ttl 非 7 或 30 |
| 400 | invalid_hash |
hash 不匹配 ^[A-Za-z0-9_-]{1,128}$ |
| 401 | unauthorized |
缺少 Authorization 或 token 不匹配 |
| 404 | not_found |
非 /upload 路径,或 GET 路径不匹配 |
| 413 | payload_too_large |
body 大于 10MB |
| 415 | unsupported_media_type |
Content-Type 非 text/html |
CLI 工具与命令:
| 入口 | 说明 |
|---|---|
artifact(延迟工具) |
读取本地文件并上传,返回 {id, url, expiresAt}。首次调用需 SearchExtraTools + ExecuteExtraTool 两步;携带上次的 id 作为 hash 可原地覆盖、URL 保持稳定 |
/artifacts |
列出本会话内上传过的制品;↑/↓ 选择、Enter 打开、c 复制 URL、Esc 退出 |
/use-artifacts |
内置技能,教模型何时 / 如何上传制品(可带可选关注点参数) |
工具输入参数(artifact):
| 参数 | 类型 | 说明 |
|---|---|---|
file_path |
string(必填) | 本地文件的绝对路径,接受 .html / .htm / .md / .markdown |
hash |
string(可选) | 覆盖已有制品,URL 保持稳定;须匹配 ^[A-Za-z0-9_-]{1,128}$ |
ttl |
7 | 30(默认 7) |
制品存活天数,只能是 7 或 30 |
实战示例
用 curl 上传(默认随机 ID + 7 天):
echo '<h1>hello</h1>' > /tmp/t.html
curl -X POST "https://cloud-artifacts.qianshou-dashi.win/upload" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: text/html" \
--data-binary @/tmp/t.html
# {"id":"V1StGXR8_Z5jdHi6B-myT",
# "url":"https://cloud-artifacts.qianshou-dashi.win/7d/V1StGXR8_Z5jdHi6B-myT.html",
# "expiresAt":"2026-06-27T10:00:00.000Z"}
自定义 hash + 30 天(再次上传同 hash 覆盖):
curl -X POST "https://cloud-artifacts.qianshou-dashi.win/upload?ttl=30&hash=my-report" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: text/html" \
--data-binary @/tmp/report.html
指定 ?hash= 时的覆盖语义:先校验字符集,再删除
7d/<hash>.html 与 30d/<hash>.html 两个旧 key
(R2 delete 不存在的 key 不报错),然后按 ?ttl= 写入新 key,并返回新的
expiresAt。不指定时用 nanoid(21) 随机 ID(126 bit 熵),
几乎不可能碰撞,不做碰撞检查。
用 CLI 的 artifact 工具上传:先让模型写本地 HTML,再上传;
后续要更新时带回第一次返回的 id 作为 hash,URL 不变。
# 1. 用 Write 工具写 HTML 到本地文件
# 2. 首次上传(延迟工具需两步)
SearchExtraTools({ query: "select:artifact" })
ExecuteExtraTool({ tool_name: "artifact", params: { file_path: "/tmp/report.html" } })
# 3. 后续更新:带回上次的 id 作为 hash,URL 保持稳定
ExecuteExtraTool({ tool_name: "artifact", params: { file_path: "/tmp/report.html", hash: "<id-from-first-call>" } })
查看本会话上传过的制品:
/artifacts
# ↑/↓ 选择 · Enter 打开 · c 复制 URL · Esc 退出
常见问题排错
经 Deno Deploy 代理时这是正常现象:代理会把上游 status code 抹平为 200,但 body 中的
{error: ...} 字段完整保留。客户端判错应以 body 的 error
字段为准,而不是 HTTP status。直连 Worker(如 *.workers.dev)时 status 正常透传。
国内 DNS 污染与路由问题导致直连 Cloudflare 边缘节点质量差。改走
cloud-artifacts.qianshou-dashi.win 出口,或为请求挂代理。
这是 Cloudflare 默认注入的 Browser Insights(RUM),不影响内容渲染。需要纯净响应时, 在 Dashboard 关闭该 Worker 的 Web Analytics。
检查 Content-Length header 是否被中间层改写;Worker 同时按
Content-Length 与 arrayBuffer().byteLength 双重校验。
设计如此:TTL 只允许 7 或 30,与 R2 lifecycle prefix 一一对应。
token 值不一致。重新执行 wrangler secret put TOKEN 设置正确值即可,
即时生效、无需 redeploy。
TOKEN 是上传侧唯一鉴权,泄露后任何人可上传 / 覆盖;GET 完全公开,URL 中的 hash
即唯一秘密;上传的 HTML 会被原样返回并执行其中的 <script>,
不要把它当作任意用户的上传入口。lifecycle rule 是 prefix 级全局规则,最长保留 30 天。