@@ -9,13 +9,15 @@ Email infrastructure for AI agents. Send and receive emails programmatically.
99
1010## Features
1111
12- - ** Send emails** via Resend (more providers coming)
12+ - ** Send emails** via Resend with attachment support
1313- ** Receive emails** via Cloudflare Email Routing Worker
14+ - ** Search inbox** — keyword search across subject, body, sender, code
1415- ** Verification code extraction** — auto-extracts codes from emails (EN/ZH/JA/KO)
15- - ** Storage providers** — local SQLite (default) or [ db9.ai] ( https://db9.ai ) cloud PostgreSQL
16- - ** Zero dependencies** — Resend provider uses raw ` fetch() ` , no SDK needed
17- - ** Agent-first** — designed for AI agents with ` skill.md ` integration guide
18- - ** Cloud service** — ` @mails.dev ` addresses with x402 micropayments (coming soon)
16+ - ** Attachments** — send files via CLI (` --attach ` ) or SDK, receive and parse MIME attachments
17+ - ** Storage providers** — local SQLite, [ db9.ai] ( https://db9.ai ) cloud PostgreSQL, or remote Worker API
18+ - ** Zero runtime dependencies** — Resend provider uses raw ` fetch() `
19+ - ** Hosted service** — free ` @mails.dev ` mailboxes via ` mails claim `
20+ - ** Self-hosted** — deploy your own Worker with optional AUTH_TOKEN
1921
2022## Install
2123
@@ -29,34 +31,55 @@ npx mails
2931
3032## Quick Start
3133
34+ ### Hosted (mails.dev)
35+
3236``` bash
33- # Configure
37+ mails claim myagent # Claim myagent@mails.dev
3438mails config set resend_api_key re_YOUR_KEY
3539mails config set default_from " Agent <agent@yourdomain.com>"
36-
37- # Send an email
3840mails send --to user@example.com --subject " Hello" --body " World"
41+ mails inbox # List received emails
42+ mails inbox --query " password" # Search emails
43+ ```
44+
45+ ### Self-Hosted
46+
47+ ``` bash
48+ cd worker && wrangler deploy # Deploy your own Worker
49+ mails config set worker_url https://your-worker.example.com
50+ mails config set worker_token YOUR_TOKEN
51+ mails config set mailbox agent@yourdomain.com
52+ mails inbox # Queries your Worker API
3953```
4054
4155## CLI Reference
4256
43- ### Send
57+ ### claim
58+
59+ ``` bash
60+ mails claim < name> # Claim name@mails.dev (max 10 per user)
61+ ```
62+
63+ ### send
4464
4565``` bash
4666mails send --to < email> --subject < subject> --body < text>
4767mails send --to < email> --subject < subject> --html " <h1>Hello</h1>"
4868mails send --from " Name <email>" --to < email> --subject < subject> --body < text>
69+ mails send --to < email> --subject " Report" --body " See attached" --attach report.pdf
4970```
5071
51- ### Inbox
72+ ### inbox
5273
5374``` bash
54- mails inbox # List recent emails
55- mails inbox --mailbox agent@test.com # Specific mailbox
56- mails inbox < id> # View email details
75+ mails inbox # List recent emails
76+ mails inbox --mailbox agent@test.com # Specific mailbox
77+ mails inbox --query " password reset" # Search emails
78+ mails inbox --query " invoice" --direction inbound --limit 10
79+ mails inbox < id> # View email details + attachments
5780```
5881
59- ### Verification Code
82+ ### code
6083
6184``` bash
6285mails code --to agent@test.com # Wait for code (default 30s)
@@ -65,19 +88,18 @@ mails code --to agent@test.com --timeout 60 # Custom timeout
6588
6689The code is printed to stdout for easy piping: ` CODE=$(mails code --to agent@test.com) `
6790
68- ### Config
91+ ### config
6992
7093``` bash
7194mails config # Show all config
7295mails config set < key> < value> # Set a value
7396mails config get < key> # Get a value
74- mails config path # Show config file path
7597```
7698
7799## SDK Usage
78100
79101``` typescript
80- import { send , getInbox , waitForCode } from ' mails'
102+ import { send , getInbox , searchInbox , waitForCode } from ' mails'
81103
82104// Send
83105const result = await send ({
@@ -86,28 +108,28 @@ const result = await send({
86108 text: ' World' ,
87109})
88110
111+ // Send with attachment
112+ await send ({
113+ to: ' user@example.com' ,
114+ subject: ' Report' ,
115+ text: ' See attached' ,
116+ attachments: [{ path: ' ./report.pdf' }],
117+ })
118+
89119// List inbox
90- const emails = await getInbox (' agent@yourdomain.com' , { limit: 10 })
120+ const emails = await getInbox (' agent@mails.dev' , { limit: 10 })
121+
122+ // Search inbox
123+ const results = await searchInbox (' agent@mails.dev' , {
124+ query: ' password reset' ,
125+ direction: ' inbound' ,
126+ })
91127
92128// Wait for verification code
93- const code = await waitForCode (' agent@yourdomain.com ' , { timeout: 30 })
129+ const code = await waitForCode (' agent@mails.dev ' , { timeout: 30 })
94130if (code ) console .log (code .code ) // "123456"
95131```
96132
97- ### Direct Provider Usage
98-
99- ``` typescript
100- import { createResendProvider } from ' mails'
101-
102- const resend = createResendProvider (' re_YOUR_KEY' )
103- await resend .send ({
104- from: ' Agent <agent@yourdomain.com>' ,
105- to: [' user@example.com' ],
106- subject: ' Hello' ,
107- text: ' Direct provider usage' ,
108- })
109- ```
110-
111133## Email Worker
112134
113135The ` worker/ ` directory contains a Cloudflare Email Routing Worker for receiving emails.
@@ -117,80 +139,78 @@ The `worker/` directory contains a Cloudflare Email Routing Worker for receiving
117139``` bash
118140cd worker
119141bun install
120- # Edit wrangler.toml — set your D1 database ID
121142wrangler d1 create mails
143+ # Edit wrangler.toml — set your D1 database ID
122144wrangler d1 execute mails --file=schema.sql
123145wrangler deploy
124146```
125147
126148Then configure Cloudflare Email Routing to forward to this worker.
127149
150+ ### Secure the Worker (optional)
151+
152+ ``` bash
153+ wrangler secret put AUTH_TOKEN # Set a secret token
154+ ```
155+
156+ If ` AUTH_TOKEN ` is set, all ` /api/* ` endpoints require ` Authorization: Bearer <token> ` . ` /health ` is always public.
157+
128158### Worker API
129159
130160| Endpoint | Description |
131161| ----------| -------------|
132162| ` GET /api/inbox?to=<addr>&limit=20 ` | List emails |
163+ | ` GET /api/inbox?to=<addr>&query=<text> ` | Search emails |
133164| ` GET /api/code?to=<addr>&timeout=30 ` | Long-poll for verification code |
134- | ` GET /api/email?id=<id> ` | Get email by ID |
135- | ` GET /health ` | Health check |
165+ | ` GET /api/email?id=<id> ` | Get email by ID (with attachments) |
166+ | ` GET /health ` | Health check (always public) |
136167
137168## Storage Providers
138169
170+ The CLI auto-detects the storage provider:
171+ - ` api_key ` in config → remote (mails.dev hosted)
172+ - ` worker_url ` in config → remote (self-hosted Worker)
173+ - Otherwise → local SQLite
174+
139175### SQLite (default)
140176
141177Local database at ` ~/.mails/mails.db ` . Zero config.
142178
143179### db9.ai
144180
145- Cloud PostgreSQL for AI agents.
181+ Cloud PostgreSQL for AI agents. Full-text search with ranking.
146182
147183``` bash
148184mails config set storage_provider db9
149185mails config set db9_token YOUR_TOKEN
150186mails config set db9_database_id YOUR_DB_ID
151187```
152188
189+ ### Remote (Worker API)
190+
191+ Queries the Worker HTTP API directly. Auto-enabled when ` api_key ` or ` worker_url ` is configured.
192+
153193## Config Keys
154194
155195| Key | Default | Description |
156196| -----| ---------| -------------|
157- | ` mode ` | ` hosted ` | ` hosted ` or ` selfhosted ` |
158- | ` domain ` | ` mails.dev ` | Email domain |
159197| ` mailbox ` | | Your receiving address |
160- | ` send_provider ` | ` resend ` | Send provider |
161- | ` storage_provider ` | ` sqlite ` | ` sqlite ` or ` db9 ` |
198+ | ` api_key ` | | API key for mails.dev hosted service |
199+ | ` worker_url ` | | Self-hosted Worker URL |
200+ | ` worker_token ` | | Auth token for self-hosted Worker |
162201| ` resend_api_key ` | | Resend API key |
163202| ` default_from ` | | Default sender address |
164- | ` db9_token ` | | db9.ai API token |
165- | ` db9_database_id ` | | db9.ai database ID |
203+ | ` storage_provider ` | auto | ` sqlite ` , ` db9 ` , or ` remote ` |
166204
167205## Testing
168206
169207``` bash
170- bun test # Run all tests (78 unit + 1 E2E)
208+ bun test # Unit + mock E2E tests
171209bun test:coverage # With coverage report
172210bun test:live # Live E2E with real Resend + Cloudflare (requires .env)
173211```
174212
175- ### Test Coverage
176-
177- ```
178- ---------------------------------|---------|---------|
179- File | % Funcs | % Lines |
180- ---------------------------------|---------|---------|
181- All files | 100.00 | 100.00 |
182- src/cli/commands/help.ts | 100.00 | 100.00 |
183- src/core/config.ts | 100.00 | 100.00 |
184- src/core/send.ts | 100.00 | 100.00 |
185- src/core/types.ts | 100.00 | 100.00 |
186- src/providers/send/resend.ts | 100.00 | 100.00 |
187- src/providers/storage/db9.ts | 100.00 | 100.00 |
188- src/providers/storage/sqlite.ts | 100.00 | 100.00 |
189- worker/src/extract-code.ts | 100.00 | 100.00 |
190- ---------------------------------|---------|---------|
191-
192- 78 unit tests + 8 live E2E tests (real Resend + Cloudflare Email Routing)
193- ```
213+ 125 unit tests + 42 E2E tests across 6 test suites.
194214
195215## License
196216
0 commit comments