Files
moemail/specs/2026-03-22-moemail-cli-design.md
ty 3d5bfad416 docs: add CI/CD publishing, agent discoverability, rename to packages/cli
- 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>
2026-03-22 14:21:29 +08:00

403 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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
```