# 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": "

Your code is 123456

", "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 email prefix (default: random) --domain email domain --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 --expiry <1h|24h|3d|permanent> --json - moemail list --json - moemail list --email-id --json - moemail wait --email-id --timeout --json - moemail read --email-id --message-id --json - moemail send --email-id --to --subject --content --json - moemail delete --email-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 ```