mirror of
https://github.com/dreamhunter2333/cloudflare_temp_email.git
synced 2026-07-09 22:42:19 +08:00
feat: add agent-mail skill, parsed mail API and docs (#993)
* feat: add cf-temp-mail-usage skill and parsed mail API for AI agents - feat: new /api/parsed_mails and /api/parsed_mail/:id endpoints returning server-parsed subject/text/html/attachments metadata (reuses commonParseMail) - feat: add .claude/skills/cf-temp-mail-usage read-only skill so AI agents (OpenClaw / Codex / Cursor) can consume a mailbox with a user-supplied JWT, bypassing the Turnstile challenge required for mailbox creation - refactor: split mails_api/index.ts and admin_api/index.ts into thin route shells; move business logic into dedicated *_api.ts files - docs: update README / README_EN / CHANGELOG with agent-email feature and npx degit install instructions for the skill Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * feat: rename skill to cf-temp-mail-agent-mail, add agent-email docs, fix sender trim - Rename skill from cf-temp-mail-usage to cf-temp-mail-agent-mail - Rewrite SKILL.md: parsed API primary, local fallback, prerequisites, multi-agent install - Add vitepress docs (zh + en) for AI Agent mailbox usage - Fix leading space in parsed_mail_api sender field via .trim() - Update README install section with 3 install methods - Update changelogs (zh + en) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * docs: simplify README agent skill section to one-liner with links Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * feat: add send mail API to skill, credential persistence, remove poll example Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -171,6 +171,7 @@ function sidebarGuide(): DefaultTheme.SidebarItem[] {
|
||||
items: [
|
||||
{ text: 'New Address API', link: 'feature/new-address-api' },
|
||||
{ text: 'View Email API', link: 'feature/mail-api' },
|
||||
{ text: 'AI Agent Mailbox Usage', link: 'feature/agent-email' },
|
||||
{ text: 'Send Email API', link: 'feature/send-mail-api' },
|
||||
{ text: 'Delete Address API', link: 'feature/delete-address' },
|
||||
]
|
||||
|
||||
@@ -171,6 +171,7 @@ function sidebarGuide(): DefaultTheme.SidebarItem[] {
|
||||
items: [
|
||||
{ text: '新建邮箱地址 API', link: 'feature/new-address-api' },
|
||||
{ text: '查看邮件 API', link: 'feature/mail-api' },
|
||||
{ text: 'AI Agent 使用邮箱', link: 'feature/agent-email' },
|
||||
{ text: '发送邮件 API', link: 'feature/send-mail-api' },
|
||||
{ text: '删除邮箱地址 API', link: 'feature/delete-address' },
|
||||
]
|
||||
|
||||
212
vitepress-docs/docs/en/guide/feature/agent-email.md
Normal file
212
vitepress-docs/docs/en/guide/feature/agent-email.md
Normal file
@@ -0,0 +1,212 @@
|
||||
# AI Agent Mailbox Usage
|
||||
|
||||
For AI agents such as OpenClaw / Codex / Cursor: consume a temp mailbox directly using a user-supplied `Address JWT + API base URL` — list the inbox, fetch a single mail, extract verification codes / magic links.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
The user must first open the frontend (e.g. `https://mail.example.com`) in a browser and **create or log into a mailbox address**. This step may require passing a Turnstile CAPTCHA that agents cannot complete automatically.
|
||||
|
||||
After creating or logging in, the **Address JWT** is displayed in the frontend UI and can be copied directly. The user provides the agent with:
|
||||
|
||||
1. **Address JWT** — copy from the frontend UI
|
||||
2. **API base URL** — same origin as the frontend, e.g. `https://mail.example.com`
|
||||
3. *(optional)* **Site password** — only if the deployment enabled `x-custom-auth`
|
||||
|
||||
### Credential persistence
|
||||
|
||||
To avoid entering credentials every time, the agent saves them to `~/.cf-temp-mail/credentials.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"base": "https://mail.example.com",
|
||||
"jwt": "<ADDRESS_JWT>",
|
||||
"site_password": ""
|
||||
}
|
||||
```
|
||||
|
||||
On first use, the agent reads the file if it exists, otherwise asks the user and saves for next time. Before each request it validates the JWT via `GET /api/settings` — if it returns `401`, the agent informs the user the JWT is expired, asks for a fresh one, and updates the file.
|
||||
|
||||
## Why `parsed_mail` API
|
||||
|
||||
By design, `/api/mails` and `/api/mail/:id` return raw RFC822 (`raw` field), so the agent must ship a MIME parser to obtain `subject` / `text` / `html`.
|
||||
|
||||
To let agents consume the mailbox directly, the project adds **server-parsed** read-only endpoints that reuse the same `postal-mime` logic used by the frontend:
|
||||
|
||||
| Task | Method | Path | Returns |
|
||||
| ----------------------- | ------ | ------------------------------------ | ----------------------------------------- |
|
||||
| Address info | GET | `/api/settings` | `{ address, send_balance }` |
|
||||
| List parsed mails | GET | `/api/parsed_mails?limit=&offset=` | `{ results: [parsedMail], count }` |
|
||||
| Get one parsed mail | GET | `/api/parsed_mail/:id` | `parsedMail` |
|
||||
|
||||
`limit` is clamped to `1..100`, `offset` is 0-based.
|
||||
|
||||
`parsedMail` shape:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 42,
|
||||
"message_id": "<...>",
|
||||
"source": "noreply@foo.com",
|
||||
"to": "abc@yourdomain.com",
|
||||
"created_at": "2026-04-21 10:00:00",
|
||||
"sender": "Foo <noreply@foo.com>",
|
||||
"subject": "Your code is 123456",
|
||||
"text": "Your code is 123456\n",
|
||||
"html": "<p>Your code is <b>123456</b></p>",
|
||||
"attachments": [
|
||||
{ "filename": "a.pdf", "mimeType": "application/pdf", "disposition": "attachment", "size": 12345 }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Attachment binary content is not included** in `parsed_*` responses — only metadata. If you need the bytes, fall back to `/api/mail/:id` and parse the raw source yourself.
|
||||
|
||||
## Required headers
|
||||
|
||||
- `Authorization: Bearer <JWT>` — required on every `/api/*` request
|
||||
- `x-custom-auth: <SITE_PASSWORD>` — only when the site enables the private password
|
||||
- `x-lang: en` or `zh` — optional, error-message language
|
||||
|
||||
::: warning Do not confuse Address JWT with User JWT
|
||||
Address JWT goes in `Authorization: Bearer`, User JWT goes in `x-user-token`. Mixing them returns `401 InvalidAddressCredentialMsg`.
|
||||
:::
|
||||
|
||||
## Examples
|
||||
|
||||
### 1. Smoke-test the JWT
|
||||
|
||||
```bash
|
||||
curl -s "$BASE/api/settings" -H "Authorization: Bearer $JWT"
|
||||
# → { "address": "abc123@example.com", "send_balance": 0 }
|
||||
```
|
||||
|
||||
If this returns `401`, the JWT is wrong / expired / mismatched with `BASE` — ask the user for a fresh one.
|
||||
|
||||
### 2. List the inbox (parsed)
|
||||
|
||||
```bash
|
||||
curl -s "$BASE/api/parsed_mails?limit=20&offset=0" \
|
||||
-H "Authorization: Bearer $JWT"
|
||||
```
|
||||
|
||||
### 3. Send mail
|
||||
|
||||
Requires `send_balance > 0` (check via `/api/settings`). The deployment must have a send method configured (Resend / SMTP / Cloudflare Email Routing binding).
|
||||
|
||||
| Task | Method | Path | Body / Returns |
|
||||
| ----------------------- | ------ | ------------------------------- | ------------------------------------------- |
|
||||
| Request send access | POST | `/api/request_send_mail_access` | `{}` → `{ status: "ok" }` |
|
||||
| Send mail | POST | `/api/send_mail` | `sendMailBody` → `{ status: "ok" }` |
|
||||
| List sent (sendbox) | GET | `/api/sendbox?limit=&offset=` | `{ results: [...], count }` |
|
||||
| Delete sent item | DELETE | `/api/sendbox/:id` | `{ success: true }` |
|
||||
|
||||
`sendMailBody`:
|
||||
|
||||
```json
|
||||
{
|
||||
"from_name": "My Name",
|
||||
"to_mail": "recipient@example.com",
|
||||
"to_name": "Recipient",
|
||||
"subject": "Hello",
|
||||
"content": "<p>Hi</p>",
|
||||
"is_html": true
|
||||
}
|
||||
```
|
||||
|
||||
`from_name` and `to_name` are optional (empty string is fine). `is_html: false` sends plain text.
|
||||
|
||||
```bash
|
||||
curl -s -X POST "$BASE/api/send_mail" \
|
||||
-H "Authorization: Bearer $JWT" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"from_name":"","to_mail":"someone@example.com","to_name":"","subject":"Test","content":"Hello","is_html":false}'
|
||||
```
|
||||
|
||||
## Fallback: local parse of raw source
|
||||
|
||||
If `/api/parsed_mails` / `/api/parsed_mail/:id` returns `404` (older deployment) or a parse error, fall back to `/api/mails` / `/api/mail/:id` (RFC822 `raw`) and **parse locally with the same strategy as the frontend**: `mail-parser-wasm` first, `postal-mime` as fallback (implementation reference: `frontend/src/utils/email-parser.js`).
|
||||
|
||||
```bash
|
||||
npm i mail-parser-wasm postal-mime
|
||||
```
|
||||
|
||||
```js
|
||||
async function parseRaw(raw) {
|
||||
try {
|
||||
const { parse_message } = await import('mail-parser-wasm');
|
||||
const m = parse_message(raw);
|
||||
if (m?.subject && (m?.body_html || m?.text)) {
|
||||
return {
|
||||
sender: m.sender || '',
|
||||
subject: m.subject || '',
|
||||
text: m.text || '',
|
||||
html: m.body_html || '',
|
||||
attachments: (m.attachments || []).map(a => ({
|
||||
filename: a.filename || a.content_id || '',
|
||||
mimeType: a.content_type || '',
|
||||
size: a.content?.length ?? 0,
|
||||
})),
|
||||
};
|
||||
}
|
||||
} catch { /* fall through */ }
|
||||
const PostalMime = (await import('postal-mime')).default;
|
||||
const p = await PostalMime.parse(raw);
|
||||
const sender = p.from?.name && p.from?.address
|
||||
? `${p.from.name} <${p.from.address}>`
|
||||
: (p.from?.address || '');
|
||||
return {
|
||||
sender,
|
||||
subject: p.subject || '',
|
||||
text: p.text || '',
|
||||
html: p.html || '',
|
||||
attachments: (p.attachments || []).map(a => ({
|
||||
filename: a.filename || a.contentId || '',
|
||||
mimeType: a.mimeType || '',
|
||||
size: a.content?.length ?? 0,
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
const row = await (await fetch(`${BASE}/api/mail/${id}`, {
|
||||
headers: { Authorization: `Bearer ${JWT}` },
|
||||
})).json();
|
||||
const parsed = await parseRaw(row.raw);
|
||||
```
|
||||
|
||||
For attachment bytes, use `postal-mime` directly — `parsed.attachments[i].content` is a `Uint8Array`.
|
||||
|
||||
## Polling discipline
|
||||
|
||||
- Start at 3s, exponential backoff capped at 10s
|
||||
- Dedupe by mail `id`
|
||||
- Never poll faster than once per second
|
||||
- Respect `429` — sleep and retry
|
||||
|
||||
## `cf-temp-mail-agent-mail` Skill
|
||||
|
||||
The repo ships an agent skill at `.claude/skills/cf-temp-mail-agent-mail/` that wraps the flow above. Works with Claude Code / Cursor / Codex / OpenClaw and other agents.
|
||||
|
||||
Pick any install method:
|
||||
|
||||
```bash
|
||||
# Option 1: npx skills (recommended, auto-detects multiple agents)
|
||||
npx skills add dreamhunter2333/cloudflare_temp_email --skill cf-temp-mail-agent-mail
|
||||
# Add -g to install globally
|
||||
npx skills add dreamhunter2333/cloudflare_temp_email --skill cf-temp-mail-agent-mail -g
|
||||
|
||||
# Option 2: npx degit to copy into your agent's skills folder
|
||||
npx degit dreamhunter2333/cloudflare_temp_email/.claude/skills/cf-temp-mail-agent-mail <your-agent-skills-dir>/cf-temp-mail-agent-mail
|
||||
|
||||
# Option 3: clone and copy
|
||||
git clone --depth 1 https://github.com/dreamhunter2333/cloudflare_temp_email.git /tmp/cf-temp-mail
|
||||
cp -r /tmp/cf-temp-mail/.claude/skills/cf-temp-mail-agent-mail <your-agent-skills-dir>/
|
||||
```
|
||||
|
||||
See [SKILL.md](https://github.com/dreamhunter2333/cloudflare_temp_email/blob/main/.claude/skills/cf-temp-mail-agent-mail/SKILL.md) for details.
|
||||
|
||||
## Common errors
|
||||
|
||||
- `401 InvalidAddressCredentialMsg` — JWT wrong / expired / sent via the wrong header. Ask the user for a fresh JWT.
|
||||
- `401 CustomAuthPasswordMsg` — site requires `x-custom-auth`; attach `SITE_PASSWORD`.
|
||||
- `400 InvalidLimitMsg` / `InvalidOffsetMsg` — `limit` must be 1..100, `offset ≥ 0`.
|
||||
- `429` — rate limited; back off and retry.
|
||||
212
vitepress-docs/docs/zh/guide/feature/agent-email.md
Normal file
212
vitepress-docs/docs/zh/guide/feature/agent-email.md
Normal file
@@ -0,0 +1,212 @@
|
||||
# AI Agent 使用临时邮箱
|
||||
|
||||
面向 OpenClaw / Codex / Cursor 等 AI Agent,让它们用用户提供的 `Address JWT + API 地址`直接消费临时邮箱:列收件箱、取单封、提取验证码/魔法链接。
|
||||
|
||||
## 前提条件
|
||||
|
||||
用户需要先在浏览器中打开前端页面(如 `https://mail.example.com`),**创建或登录一个邮箱地址**。这一步可能需要通过 Turnstile 人机验证,Agent 无法自动完成。
|
||||
|
||||
创建/登录成功后,**Address JWT** 会显示在前端界面上,可直接复制。用户需要提供给 Agent:
|
||||
|
||||
1. **Address JWT** — 从前端界面复制
|
||||
2. **API 地址** — 与前端同源,如 `https://mail.example.com`
|
||||
3. *(可选)* **站点密码** — 仅当部署启用了 `x-custom-auth` 时需要
|
||||
|
||||
### 凭证持久化
|
||||
|
||||
为避免每次都要输入,Agent 会将凭证保存到 `~/.cf-temp-mail/credentials.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"base": "https://mail.example.com",
|
||||
"jwt": "<ADDRESS_JWT>",
|
||||
"site_password": ""
|
||||
}
|
||||
```
|
||||
|
||||
首次使用时如果文件存在则直接读取,不存在则向用户索要后保存。每次请求前通过 `GET /api/settings` 校验 JWT,若返回 `401` 则提示用户 JWT 已过期并更新文件。
|
||||
|
||||
## 为什么需要 `parsed_mail` API
|
||||
|
||||
`/api/mails` 与 `/api/mail/:id` 按设计返回原始 RFC822(`raw` 字段),Agent 侧需要自己解析 MIME 才能拿到 `subject`/`text`/`html`。
|
||||
|
||||
为方便 Agent 直接消费,项目新增了**服务端解析**的只读接口,复用前端同款的 `postal-mime` 解析逻辑:
|
||||
|
||||
| 任务 | 方法 | 路径 | 返回 |
|
||||
| ------------ | ---- | ------------------------------------ | ----------------------------------------- |
|
||||
| 地址信息 | GET | `/api/settings` | `{ address, send_balance }` |
|
||||
| 列出解析邮件 | GET | `/api/parsed_mails?limit=&offset=` | `{ results: [parsedMail], count }` |
|
||||
| 取单封解析 | GET | `/api/parsed_mail/:id` | `parsedMail` |
|
||||
|
||||
`limit` 范围 `1..100`,`offset` 从 0 开始。
|
||||
|
||||
`parsedMail` 结构:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 42,
|
||||
"message_id": "<...>",
|
||||
"source": "noreply@foo.com",
|
||||
"to": "abc@yourdomain.com",
|
||||
"created_at": "2026-04-21 10:00:00",
|
||||
"sender": "Foo <noreply@foo.com>",
|
||||
"subject": "Your code is 123456",
|
||||
"text": "Your code is 123456\n",
|
||||
"html": "<p>Your code is <b>123456</b></p>",
|
||||
"attachments": [
|
||||
{ "filename": "a.pdf", "mimeType": "application/pdf", "disposition": "attachment", "size": 12345 }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**附件二进制不包含**在 `parsed_*` 响应里,只有元数据。需要原始字节时再退回 `/api/mail/:id` 自己解析。
|
||||
|
||||
## 必要的请求头
|
||||
|
||||
- `Authorization: Bearer <JWT>` — 所有 `/api/*` 请求必须携带
|
||||
- `x-custom-auth: <SITE_PASSWORD>` — 仅当站点启用了私有密码
|
||||
- `x-lang: en` 或 `zh` — 可选,报错信息语言
|
||||
|
||||
::: warning 不要把 Address JWT 当 User JWT 用
|
||||
Address JWT 走 `Authorization: Bearer`,用户 JWT 走 `x-user-token`,两种凭证不可混用,否则返回 `401 InvalidAddressCredentialMsg`。
|
||||
:::
|
||||
|
||||
## 示例
|
||||
|
||||
### 1. 自检 JWT
|
||||
|
||||
```bash
|
||||
curl -s "$BASE/api/settings" -H "Authorization: Bearer $JWT"
|
||||
# → { "address": "abc123@example.com", "send_balance": 0 }
|
||||
```
|
||||
|
||||
返回 `401` 说明 JWT 错/过期/和 `BASE` 不匹配,请用户重新提供。
|
||||
|
||||
### 2. 列表(解析后)
|
||||
|
||||
```bash
|
||||
curl -s "$BASE/api/parsed_mails?limit=20&offset=0" \
|
||||
-H "Authorization: Bearer $JWT"
|
||||
```
|
||||
|
||||
### 3. 发送邮件
|
||||
|
||||
需要 `send_balance > 0`(通过 `/api/settings` 查看),且部署方已配置发送方式(Resend / SMTP / Cloudflare Email Routing binding)。
|
||||
|
||||
| 任务 | 方法 | 路径 | 请求体 / 返回 |
|
||||
| ---------------- | ------ | ------------------------------- | ------------------------------------------ |
|
||||
| 申请发信权限 | POST | `/api/request_send_mail_access` | `{}` → `{ status: "ok" }` |
|
||||
| 发送邮件 | POST | `/api/send_mail` | `sendMailBody` → `{ status: "ok" }` |
|
||||
| 列出已发送 | GET | `/api/sendbox?limit=&offset=` | `{ results: [...], count }` |
|
||||
| 删除已发送 | DELETE | `/api/sendbox/:id` | `{ success: true }` |
|
||||
|
||||
`sendMailBody`:
|
||||
|
||||
```json
|
||||
{
|
||||
"from_name": "My Name",
|
||||
"to_mail": "recipient@example.com",
|
||||
"to_name": "Recipient",
|
||||
"subject": "Hello",
|
||||
"content": "<p>Hi</p>",
|
||||
"is_html": true
|
||||
}
|
||||
```
|
||||
|
||||
`from_name` 和 `to_name` 可选(空字符串即可)。`is_html: false` 发送纯文本。
|
||||
|
||||
```bash
|
||||
curl -s -X POST "$BASE/api/send_mail" \
|
||||
-H "Authorization: Bearer $JWT" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"from_name":"","to_mail":"someone@example.com","to_name":"","subject":"Test","content":"Hello","is_html":false}'
|
||||
```
|
||||
|
||||
## 回退方案:本地解析 raw
|
||||
|
||||
若 `/api/parsed_mails` / `/api/parsed_mail/:id` 返回 `404`(较早部署未包含)或解析异常,回退到 `/api/mails` / `/api/mail/:id` 取 `raw`,**在本地按前端同款策略解析**:`mail-parser-wasm` 优先,失败时退回 `postal-mime`(实现参见 `frontend/src/utils/email-parser.js`)。
|
||||
|
||||
```bash
|
||||
npm i mail-parser-wasm postal-mime
|
||||
```
|
||||
|
||||
```js
|
||||
async function parseRaw(raw) {
|
||||
try {
|
||||
const { parse_message } = await import('mail-parser-wasm');
|
||||
const m = parse_message(raw);
|
||||
if (m?.subject && (m?.body_html || m?.text)) {
|
||||
return {
|
||||
sender: m.sender || '',
|
||||
subject: m.subject || '',
|
||||
text: m.text || '',
|
||||
html: m.body_html || '',
|
||||
attachments: (m.attachments || []).map(a => ({
|
||||
filename: a.filename || a.content_id || '',
|
||||
mimeType: a.content_type || '',
|
||||
size: a.content?.length ?? 0,
|
||||
})),
|
||||
};
|
||||
}
|
||||
} catch { /* fall through */ }
|
||||
const PostalMime = (await import('postal-mime')).default;
|
||||
const p = await PostalMime.parse(raw);
|
||||
const sender = p.from?.name && p.from?.address
|
||||
? `${p.from.name} <${p.from.address}>`
|
||||
: (p.from?.address || '');
|
||||
return {
|
||||
sender,
|
||||
subject: p.subject || '',
|
||||
text: p.text || '',
|
||||
html: p.html || '',
|
||||
attachments: (p.attachments || []).map(a => ({
|
||||
filename: a.filename || a.contentId || '',
|
||||
mimeType: a.mimeType || '',
|
||||
size: a.content?.length ?? 0,
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
const row = await (await fetch(`${BASE}/api/mail/${id}`, {
|
||||
headers: { Authorization: `Bearer ${JWT}` },
|
||||
})).json();
|
||||
const parsed = await parseRaw(row.raw);
|
||||
```
|
||||
|
||||
需要附件字节时直接用 `postal-mime`——`parsed.attachments[i].content` 是 `Uint8Array`。
|
||||
|
||||
## 轮询纪律
|
||||
|
||||
- 初始 3s 起步,指数退避,封顶 10s
|
||||
- 按 `id` 去重
|
||||
- 不要快于每秒 1 次
|
||||
- 遇到 `429` 必须 sleep 后重试
|
||||
|
||||
## `cf-temp-mail-agent-mail` Skill
|
||||
|
||||
仓库内置了 Agent 技能:`.claude/skills/cf-temp-mail-agent-mail/`,把上述流程封装成 AI Agent 可直接调用的形式,支持 Claude Code / Cursor / Codex / OpenClaw 等。
|
||||
|
||||
安装方式任选其一:
|
||||
|
||||
```bash
|
||||
# 方式 1:npx skills(推荐,自动适配多种 agent)
|
||||
npx skills add dreamhunter2333/cloudflare_temp_email --skill cf-temp-mail-agent-mail
|
||||
# 加 -g 安装到全局
|
||||
npx skills add dreamhunter2333/cloudflare_temp_email --skill cf-temp-mail-agent-mail -g
|
||||
|
||||
# 方式 2:npx degit 拷贝到你的 agent skills 目录
|
||||
npx degit dreamhunter2333/cloudflare_temp_email/.claude/skills/cf-temp-mail-agent-mail <your-agent-skills-dir>/cf-temp-mail-agent-mail
|
||||
|
||||
# 方式 3:克隆后复制
|
||||
git clone --depth 1 https://github.com/dreamhunter2333/cloudflare_temp_email.git /tmp/cf-temp-mail
|
||||
cp -r /tmp/cf-temp-mail/.claude/skills/cf-temp-mail-agent-mail <your-agent-skills-dir>/
|
||||
```
|
||||
|
||||
详情见 [SKILL.md](https://github.com/dreamhunter2333/cloudflare_temp_email/blob/main/.claude/skills/cf-temp-mail-agent-mail/SKILL.md)。
|
||||
|
||||
## 常见错误
|
||||
|
||||
- `401 InvalidAddressCredentialMsg` — JWT 错/过期/header 填错,让用户重新提供
|
||||
- `401 CustomAuthPasswordMsg` — 站点启用了 `x-custom-auth`,附带 `SITE_PASSWORD`
|
||||
- `400 InvalidLimitMsg` / `InvalidOffsetMsg` — `limit` 必须 1..100,`offset ≥ 0`
|
||||
- `429` — 被限流,退避后重试
|
||||
Reference in New Issue
Block a user