mirror of
https://github.com/beilunyang/moemail.git
synced 2026-06-10 01:50:26 +08:00
- Add publish-cli.yml workflow triggered by cli-v* tags - Add agent discoverability section (help, README, llms.txt) - Rename packages/moemail-cli to packages/cli Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
403 lines
12 KiB
Markdown
403 lines
12 KiB
Markdown
# MoeMail CLI — Agent-First Command Line Tool
|
||
|
||
## Overview
|
||
|
||
A CLI tool that wraps MoeMail's existing OpenAPI, optimized for AI Agent workflows. Agents can create temporary emails, wait for incoming messages, read content, and manage mailboxes through simple shell commands.
|
||
|
||
**Goal:** Make MoeMail a first-class tool in any AI Agent's toolchain with minimal friction — one command per action, structured JSON output, minimal server-side changes.
|
||
|
||
**Server-side changes:** One small change required — the `POST /api/emails/{id}/send` endpoint currently uses session-only auth. It needs to be updated to also support API Key auth (switch from `auth()` to `getUserId()`) so the CLI `send` command works.
|
||
|
||
## Architecture
|
||
|
||
```
|
||
Agent (Claude / GPT / Custom)
|
||
↓ shell call
|
||
moemail CLI (npm package, bun-built single JS file)
|
||
↓ HTTPS + X-API-Key header
|
||
MoeMail Server (existing Next.js API, one minor auth change for send endpoint)
|
||
↓
|
||
Cloudflare D1 / Email Workers
|
||
```
|
||
|
||
- **Language:** TypeScript
|
||
- **Build:** `bun build ./src/index.ts --outdir ./dist --target=node`
|
||
- **Distribution:** npm package, `npm i -g moemail-cli`
|
||
- **Binary:** `package.json` `bin` field points to `dist/index.js`
|
||
- **Location:** `packages/cli/` in the monorepo
|
||
- **Server changes:** One change — update send endpoint to support API Key auth.
|
||
|
||
## Configuration
|
||
|
||
Stored at `~/.moemail/config.json`:
|
||
|
||
```json
|
||
{
|
||
"apiUrl": "https://moemail.app",
|
||
"apiKey": "mk_xxxxxxxx"
|
||
}
|
||
```
|
||
|
||
Environment variable overrides (higher priority):
|
||
- `MOEMAIL_API_URL`
|
||
- `MOEMAIL_API_KEY`
|
||
|
||
## Commands
|
||
|
||
All commands support `--json` for JSON output, `--help` for usage info, and `moemail --version` for version.
|
||
|
||
**Field naming convention:** CLI JSON output uses camelCase (`messageId`, `receivedAt`, `fromAddress`). The API uses snake_case (`message_id`, `received_at`, `from_address`). The CLI transforms all field names to camelCase, and converts epoch ms timestamps to ISO 8601 strings.
|
||
|
||
### `moemail config`
|
||
|
||
```bash
|
||
# Interactive setup
|
||
moemail config
|
||
|
||
# Direct set
|
||
moemail config set api-url https://moemail.app
|
||
moemail config set api-key mk_xxxxxxxx
|
||
|
||
# View current config
|
||
moemail config list
|
||
```
|
||
|
||
### `moemail create`
|
||
|
||
```bash
|
||
# Random prefix, 1h expiry (defaults)
|
||
moemail create
|
||
|
||
# With parameters
|
||
moemail create --name test --domain moemail.app --expiry 24h
|
||
```
|
||
|
||
`--expiry` options and API mapping:
|
||
|
||
| CLI flag | API `expiryTime` (ms) |
|
||
|----------|----------------------|
|
||
| `1h` | `3600000` |
|
||
| `24h` | `86400000` |
|
||
| `3d` | `259200000` |
|
||
| `permanent` | `0` |
|
||
|
||
Default output:
|
||
```
|
||
Created: test@moemail.app (expires in 24 hours)
|
||
ID: abc-123
|
||
```
|
||
|
||
JSON output (`--json`):
|
||
```json
|
||
{"id": "abc-123", "address": "test@moemail.app", "expiresAt": "2026-03-23T12:00:00Z"}
|
||
```
|
||
|
||
Note: The API returns `{ id, email }`. The CLI renames `email` → `address` for clarity, and computes `expiresAt` from the chosen expiry option.
|
||
|
||
### `moemail list`
|
||
|
||
```bash
|
||
# List all mailboxes
|
||
moemail list
|
||
|
||
# List messages in a mailbox
|
||
moemail list --email-id xxx
|
||
|
||
# With pagination
|
||
moemail list --cursor xxx
|
||
```
|
||
|
||
JSON output — mailboxes (`--json`):
|
||
```json
|
||
{"emails": [{"id": "abc-123", "address": "test@moemail.app", "expiresAt": "..."}], "nextCursor": "xxx", "total": 5}
|
||
```
|
||
|
||
JSON output — messages (`--json --email-id xxx`):
|
||
```json
|
||
{"messages": [{"id": "msg-1", "from": "sender@example.com", "subject": "Hello", "receivedAt": "..."}], "nextCursor": null, "total": 2}
|
||
```
|
||
|
||
### `moemail wait`
|
||
|
||
The core command for Agent workflows. Polls the API until a new message arrives or timeout.
|
||
|
||
```bash
|
||
# Wait for new message (default: 120s timeout, 5s interval)
|
||
moemail wait --email-id xxx
|
||
|
||
# Custom timeout and interval
|
||
moemail wait --email-id xxx --timeout 60 --interval 3
|
||
```
|
||
|
||
**Parameters:** `--timeout` and `--interval` are in seconds.
|
||
|
||
**Behavior:**
|
||
1. Fetch current message list, record all existing message IDs in a Set
|
||
2. Poll `GET /api/emails/{id}` every `--interval` seconds
|
||
3. Compare returned message IDs against the initial Set to detect new messages (ID-based, not count-based — safe against concurrent deletes)
|
||
4. On new message: output message summary and exit with code 0
|
||
5. On timeout: exit with code 1
|
||
|
||
Default output:
|
||
```
|
||
Polling... (15/120s)
|
||
New message from no-reply@github.com: "Verify your email"
|
||
Message ID: msg-456
|
||
```
|
||
|
||
JSON output (`--json`):
|
||
```json
|
||
{"messageId": "msg-456", "from": "no-reply@github.com", "subject": "Verify your email", "receivedAt": "2026-03-22T12:05:00Z"}
|
||
```
|
||
|
||
### `moemail read`
|
||
|
||
```bash
|
||
# Read message (default: plain text)
|
||
moemail read --email-id xxx --message-id yyy
|
||
|
||
# HTML format
|
||
moemail read --email-id xxx --message-id yyy --format html
|
||
```
|
||
|
||
`--format` options: `text` (default) | `html`
|
||
|
||
JSON output (`--json`):
|
||
```json
|
||
{"id": "msg-456", "from": "no-reply@github.com", "to": "test@moemail.app", "subject": "Verify your email", "content": "Your code is 123456", "html": "<p>Your code is 123456</p>", "receivedAt": "2026-03-22T12:05:00Z", "type": "received"}
|
||
```
|
||
|
||
### `moemail send`
|
||
|
||
```bash
|
||
moemail send --email-id xxx --to user@example.com --subject "Hello" --content "Body text"
|
||
```
|
||
|
||
JSON output (`--json`):
|
||
```json
|
||
{"success": true, "remainingEmails": 4}
|
||
```
|
||
|
||
### `moemail delete`
|
||
|
||
```bash
|
||
# Delete mailbox and all messages
|
||
moemail delete --email-id xxx
|
||
|
||
# Delete single message
|
||
moemail delete --email-id xxx --message-id yyy
|
||
```
|
||
|
||
JSON output (`--json`):
|
||
```json
|
||
{"success": true}
|
||
```
|
||
|
||
## Output Specification
|
||
|
||
### Modes
|
||
|
||
| Mode | Flag | Format | Use case |
|
||
|------|------|--------|----------|
|
||
| Text | (default) | Human-readable | Debugging, manual use |
|
||
| JSON | `--json` | Single-line JSON on stdout | Agent consumption |
|
||
|
||
### Exit Codes
|
||
|
||
| Code | Meaning |
|
||
|------|---------|
|
||
| 0 | Success |
|
||
| 1 | Operation failed (timeout, not found, bad params) |
|
||
| 2 | Auth failed (invalid/expired API Key) |
|
||
|
||
### Error Handling
|
||
|
||
- Errors write to **stderr**, never stdout
|
||
- In `--json` mode, stdout contains only valid JSON (or nothing on failure)
|
||
- Agent reads stdout for data, checks exit code for success/failure
|
||
|
||
```bash
|
||
$ moemail wait --email-id xxx --timeout 10 --json
|
||
# stderr: Polling... (3/10s)
|
||
# stderr: Timeout: no new messages received
|
||
# stdout: (empty)
|
||
# exit code: 1
|
||
```
|
||
|
||
## Project Structure
|
||
|
||
```
|
||
packages/cli/
|
||
├── src/
|
||
│ ├── index.ts # Entry point, command registration
|
||
│ ├── commands/
|
||
│ │ ├── config.ts # config command
|
||
│ │ ├── create.ts # create command
|
||
│ │ ├── list.ts # list command
|
||
│ │ ├── wait.ts # wait command (client-side polling)
|
||
│ │ ├── read.ts # read command
|
||
│ │ ├── send.ts # send command
|
||
│ │ └── delete.ts # delete command
|
||
│ ├── api.ts # HTTP client wrapping all API calls
|
||
│ ├── config.ts # Read/write ~/.moemail/config.json
|
||
│ └── output.ts # Output formatting (text / json)
|
||
├── package.json
|
||
├── tsconfig.json
|
||
└── README.md
|
||
```
|
||
|
||
## Dependencies
|
||
|
||
- **commander** — CLI argument parsing and subcommand routing
|
||
- All other needs (HTTP, filesystem) use Node.js built-in APIs
|
||
|
||
## Relationship to Main Project
|
||
|
||
- Lives in `packages/cli/`, published as a separate npm package
|
||
- No code shared with the main Next.js app
|
||
- Only coupling is the API contract (URLs, request/response shapes)
|
||
- Server-side: one change — send endpoint auth support for API Key
|
||
|
||
## CI/CD Publishing
|
||
|
||
CLI 通过独立的 GitHub Actions workflow 发布到 npm,与主项目的 deploy workflow 分开。
|
||
|
||
### Workflow: `.github/workflows/publish-cli.yml`
|
||
|
||
**触发条件:**
|
||
- Push tag 匹配 `cli-v*`(如 `cli-v1.0.0`),与主项目的 `v*` tag 区分
|
||
|
||
**流程:**
|
||
1. Checkout 代码
|
||
2. Setup pnpm + Node.js 20
|
||
3. Install dependencies(`pnpm install --frozen-lockfile`)
|
||
4. Build CLI(`cd packages/cli && bun build ./src/index.ts --outdir ./dist --target=node`)
|
||
5. Publish to npm(`cd packages/cli && npm publish --access public`)
|
||
|
||
**所需 Secrets:**
|
||
- `NPM_TOKEN`:npm publish token,在 GitHub repo Settings → Secrets 中配置
|
||
|
||
### 发布步骤
|
||
|
||
```bash
|
||
# 1. 更新 packages/cli/package.json 中的 version
|
||
# 2. Commit & push
|
||
# 3. 打 tag 并推送
|
||
git tag cli-v1.0.0
|
||
git push origin cli-v1.0.0
|
||
```
|
||
|
||
### 版本策略
|
||
|
||
CLI 独立版本号,不与主项目同步。遵循 semver:
|
||
- **patch**:bug fix
|
||
- **minor**:新增命令或 flag
|
||
- **major**:破坏性变更(命令重命名、输出格式变更)
|
||
|
||
## Agent Discoverability
|
||
|
||
三层机制让 AI Agent 知道如何使用 CLI。
|
||
|
||
### 1. CLI 内置 Help
|
||
|
||
Commander 自动生成,Agent 调用一次即可获取完整用法:
|
||
|
||
```bash
|
||
$ moemail --help
|
||
Usage: moemail [options] [command]
|
||
|
||
MoeMail CLI — Agent-friendly temporary email tool
|
||
|
||
Options:
|
||
-V, --version output the version number
|
||
--json output as JSON
|
||
-h, --help display help for command
|
||
|
||
Commands:
|
||
config configure API endpoint and API Key
|
||
create [options] create a temporary email address
|
||
list [options] list mailboxes or messages
|
||
wait [options] wait for a new email to arrive
|
||
read [options] read an email message
|
||
send [options] send an email from a temporary address
|
||
delete [options] delete a mailbox or message
|
||
|
||
$ moemail create --help
|
||
Usage: moemail create [options]
|
||
|
||
Create a temporary email address
|
||
|
||
Options:
|
||
--name <name> email prefix (default: random)
|
||
--domain <domain> email domain
|
||
--expiry <expiry> 1h | 24h | 3d | permanent (default: "1h")
|
||
--json output as JSON
|
||
-h, --help display help for command
|
||
```
|
||
|
||
### 2. README 文档
|
||
|
||
`packages/cli/README.md` 作为 npm 包首页展示,包含:
|
||
- 一句话介绍:Agent-first CLI for MoeMail temporary email service
|
||
- 安装命令
|
||
- 快速开始(3 步:config → create → wait)
|
||
- 完整命令参考表
|
||
- Agent workflow 示例
|
||
- JSON 输出格式说明
|
||
|
||
### 3. llms.txt
|
||
|
||
在 MoeMail 站点根目录提供 `https://moemail.app/llms.txt`,遵循 llms.txt 协议。Agent 访问网站时自动发现可用工具。
|
||
|
||
```
|
||
# MoeMail
|
||
|
||
> Temporary email service with CLI tool for AI Agents
|
||
|
||
MoeMail provides disposable email addresses. Install the CLI for programmatic access:
|
||
|
||
## CLI Tool
|
||
|
||
Install: npm i -g moemail-cli
|
||
|
||
Setup: moemail config set api-url https://moemail.app && moemail config set api-key YOUR_KEY
|
||
|
||
Commands:
|
||
- moemail create --domain <domain> --expiry <1h|24h|3d|permanent> --json
|
||
- moemail list --json
|
||
- moemail list --email-id <id> --json
|
||
- moemail wait --email-id <id> --timeout <seconds> --json
|
||
- moemail read --email-id <id> --message-id <id> --json
|
||
- moemail send --email-id <id> --to <addr> --subject <subj> --content <body> --json
|
||
- moemail delete --email-id <id> --json
|
||
|
||
Typical workflow: create email → use address for signup → wait for verification → read content → extract code → delete
|
||
|
||
All commands support --json for structured output. Exit code 0 = success, 1 = failure, 2 = auth error.
|
||
```
|
||
|
||
**实现方式:** 在 Next.js 的 `public/` 目录下放置 `llms.txt` 静态文件,部署时自动可访问。
|
||
|
||
## Typical Agent Workflow
|
||
|
||
```bash
|
||
# 1. Create a temporary mailbox
|
||
EMAIL=$(moemail create --domain moemail.app --expiry 1h --json)
|
||
EMAIL_ID=$(echo $EMAIL | jq -r '.id')
|
||
ADDRESS=$(echo $EMAIL | jq -r '.address')
|
||
|
||
# 2. Use the address to sign up for a service (agent does this elsewhere)
|
||
|
||
# 3. Wait for verification email
|
||
MSG=$(moemail wait --email-id $EMAIL_ID --timeout 120 --json)
|
||
MSG_ID=$(echo $MSG | jq -r '.messageId')
|
||
|
||
# 4. Read the email content
|
||
CONTENT=$(moemail read --email-id $EMAIL_ID --message-id $MSG_ID --json)
|
||
|
||
# 5. Agent extracts verification code from content (LLM does this)
|
||
|
||
# 6. Clean up
|
||
moemail delete --email-id $EMAIL_ID
|
||
```
|