Inbox triage as a local app: log in to Gmail / Outlook / 163 and clear emails with 3-version LLM-drafted replies (DeepSeek-powered).
Project description
imail
Inbox triage as a local app. Log in once to Gmail, Outlook, or 163 — then clear your unread emails by picking from three LLM-drafted replies per message. Powered by DeepSeek.
You wake up to 30 emails. Each one needs a yes / no / "let me get back to you." imail fetches
your unread inbox, asks the model for three reply versions per email (positive · neutral · negative),
and saves your pick as a Gmail/IMAP draft. You stay in control of every Send button.
┌──────────────────────────────────────────────────────────────┐
│ From advisor@uni.edu │
│ Subject Can you join the panel on Thursday? │
│ … │
└──────────────────────────────────────────────────────────────┘
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 1·POSITIVE │ │ 2·NEUTRAL │ │ 3·NEGATIVE │
│ Yes, I'll … │ │ Let me … │ │ Thanks for │
│ │ │ │ │ asking, but │
└─────────────┘ └─────────────┘ └─────────────┘
1 / 2 / 3 → save draft S → skip Q → end session
Why this exists
Email is mostly acknowledging or deferring — yes / no / "let me check
and get back to you." Each one of those takes 30 seconds of phrasing
even though the decision takes one. imail flips the workflow so
phrasing is one keystroke and you're left with the decision:
- 3 drafts in one round-trip. DeepSeek sees the email once and returns positive / neutral / negative together — one API call, three angles, ~$0.0002. The model never sees a second prompt to "rephrase more polite" or "try again less eager".
- Your mailbox stays where it is. imail logs into your existing Gmail / Outlook / 163 via OAuth or IMAP. Saved drafts land in the same Drafts folder you already use — they show up on your phone too.
- Local-first. Email content stays on your machine. The only network hop is to DeepSeek for the draft itself. No imail server, no inbox copy uploaded anywhere.
- One UI across providers. Three reply tones, the same keyboard shortcuts (1 / 2 / 3 / S / Q), whether your work mail is Microsoft 365 and your personal is Gmail or your school is 163.
- Persists what you've handled. Already-replied emails are tagged with a green "Replied" badge in the inbox and surfaced in Sent so you can re-read the reply you actually chose — no token waste on re-drafting.
Features
- ✦ Multi-provider — Gmail (OAuth), and any IMAP mailbox: Outlook, 163, 126, QQ, Yahoo, iCloud, custom hosts.
- ✦ Local web app —
imailboots a FastAPI server and pops your browser tohttp://127.0.0.1:8765. - ✦ Drafts only — never sends mail on your behalf. You press Send yourself in your normal mail client.
- ✦ Secrets in OS keyring — IMAP app passwords go to macOS Keychain (or platform equivalent), not a plain text file.
- ✦ DeepSeek by default — cheap, fast, OpenAI-compatible. Point
IMAIL_BASE_URLat any other OpenAI-compatible endpoint (Together, Groq, Moonshot, 302.AI, an Anthropic-proxy, etc.) and it just works. - ✦ Three drafts per email in a single API call — fast, cheap, self-consistent. DeepSeek auto-caches the system prefix server-side.
- ✦ Keyboard-first —
1/2/3to save a draft,Sto skip,Qto end.
Install
The easy way (any platform — macOS / Linux / Windows; just needs Python ≥ 3.11):
# Recommended: uv (fastest, no extra setup if you already have it).
uv tool install imail-cli
# Or pipx (if you don't have uv).
pipx install imail-cli
Note: distribution name on PyPI is
imail-cli(the bareimailslot was taken). The command you run after install is stillimail.
Then export your DeepSeek API key and launch:
export DEEPSEEK_API_KEY=sk-... # get one at https://platform.deepseek.com/api_keys
imail # opens http://127.0.0.1:8765
First run shows an "Add account" screen. Pick Gmail / Outlook / 163 / etc., sign in, and you're triaging.
Try without installing (uv only):
uvx --from imail-cli imail
Or run via Docker (no Python install needed):
docker run --rm -p 8765:8765 \
-v ~/.config/imail:/root/.config/imail \
-e DEEPSEEK_API_KEY=sk-... \
ghcr.io/jessecu2024/imail:latest
# → open http://localhost:8765 in your browser
The mounted ~/.config/imail keeps your accounts + saved replies between
container restarts.
Releases are also attached as
.whland.tar.gzon GitHub Releases if you'd rather download the artefact yourself. See CHANGELOG.md for what changed in each version.
Quick Start (from source)
# 1. Install deps
uv sync
# 2. Set your DeepSeek key
cp .env.example .env
# → put your DEEPSEEK_API_KEY in .env (get one at https://platform.deepseek.com/api_keys)
# 3. Launch the app
uv run imail
# → opens http://127.0.0.1:8765 in your browser
First run will show an "Add account" screen. Pick a provider:
- Gmail — drop your
credentials.json(see docs/gmail-setup.md) and the first triage triggers the OAuth consent screen. - Microsoft 365 / Outlook / 163 / 126 / QQ / Yahoo / iCloud — sign in with your email + an app password / 授权码 (see docs/imap-setup.md).
- Custom — enter any IMAPS host + port.
After that, click your mailbox card → walk through unread emails → press 1/2/3 → drafts land in your Drafts folder.
Work or school account that blocks IMAP and won't let you register an Azure app? (CityU and many other universities.) See docs/forwarding-workflow.md for the validated forwarding-to-personal fallback — adds your work mail to imail in 5 minutes of server-side setup, no code needed.
Where this project is heading + what would unblock more mailboxes: docs/vision-and-paths.md — the project's big picture, current capability matrix, and a ranked list of compromise paths (browser extension, bookmarklet, menu-bar app, Outlook add-in, etc.) for when direct API access is blocked.
Configuration
All settings come from .env (see .env.example):
| Variable | Default | Notes |
|---|---|---|
DEEPSEEK_API_KEY |
(required) | DeepSeek API key. OPENAI_API_KEY is a fallback for non-DeepSeek endpoints. |
IMAIL_BASE_URL |
https://api.deepseek.com |
OpenAI-compatible endpoint |
IMAIL_MODEL |
deepseek-chat |
Or deepseek-reasoner for sharper, slower drafts |
USER_SIGNOFF |
Jie |
Name used in reply sign-offs |
IMAIL_PORT |
8765 |
Local server port |
IMAIL_HOST |
127.0.0.1 |
Bind address (only listens on loopback by default) |
IMAIL_CONFIG_DIR |
~/.config/imail |
Where accounts.json and OAuth tokens live |
Development
uv sync # install dev + runtime deps
uv run ruff check . # lint
uv run ruff format . # format
uv run mypy src # type check
uv run pytest # tests
Project Structure
src/imail/
cli.py Launcher — boots uvicorn + opens browser
config.py .env loader
server.py FastAPI app — status, accounts, triage session
accounts.py Account manifest + keyring-backed secrets
reply_generator.py DeepSeek (OpenAI-compatible) call + JSON parser
providers/
base.py EmailMsg + MailProvider Protocol
gmail.py Gmail API provider (OAuth)
imap.py Generic IMAP provider with presets
static/
index.html Single-page UI
app.js Alpine.js component
style.css Dark theme
docs/
gmail-setup.md One-time Google Cloud Console walk-through
imap-setup.md How to get app passwords on each provider
tests/ pytest suites
Security Notes
- No
gmail.sendscope is requested; the Gmail provider can only fetch, draft, mark-read, archive. - IMAP app passwords are stored in the OS keyring, not in
accounts.jsonor.env. - OAuth tokens are saved to
~/.config/imail/token-<account-id>.json(mode 0600). - Server listens on
127.0.0.1by default. Don't move it to0.0.0.0on a shared machine. - Email content reaches DeepSeek's servers. If you'd rather not share message bodies with any third party, point
IMAIL_BASE_URLat a self-hosted Ollama / vLLM endpoint running an instruction model.
License
Dual-licensed. Pick whichever fits your use:
- AGPL-3.0-or-later (default, free) — full text in LICENSE. You're free to use, modify, fork, and self-host imail. The catch: if you modify the code and either redistribute it or run it as a network service (a SaaS, a hosted product, anything end users reach over a network), you must release your modifications under AGPL-3.0 too. This is the standard "no closed-source forks, no proprietary SaaS rebrand" guarantee — see the GNU AGPL FAQ for the rationale.
- Commercial license — if AGPL's copyleft obligations don't fit your product (you want to ship a proprietary version, host imail inside a closed-source SaaS, or distribute imail as a library without exposing your own source), email wzh4464@gmail.com with what you'd like to do and we'll work out a commercial license.
Versions ≤ 1.3.0 on PyPI were published under MIT. From v1.3.1 onward the license switches to AGPL-3.0-or-later (or commercial). If you installed
imail-cli==1.3.0you remain on MIT terms for that copy.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file imail_cli-1.3.1.tar.gz.
File metadata
- Download URL: imail_cli-1.3.1.tar.gz
- Upload date:
- Size: 319.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
75bd084ac18f43d216b2dd8f37f161dc508806b742b30d28a80bbce57cdba8d8
|
|
| MD5 |
923ff861517509db1688426c43b3a027
|
|
| BLAKE2b-256 |
c8dc37281c8e3ee3b458062de4ab36f6ea8931810dabd12b0e66c2ce2f424a23
|
Provenance
The following attestation bundles were made for imail_cli-1.3.1.tar.gz:
Publisher:
release.yml on jessecu2024/imail
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
imail_cli-1.3.1.tar.gz -
Subject digest:
75bd084ac18f43d216b2dd8f37f161dc508806b742b30d28a80bbce57cdba8d8 - Sigstore transparency entry: 1573239422
- Sigstore integration time:
-
Permalink:
jessecu2024/imail@ca6be9f05b46367ade8c90766c9ff03f7ebe5fee -
Branch / Tag:
refs/tags/v1.3.1 - Owner: https://github.com/jessecu2024
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@ca6be9f05b46367ade8c90766c9ff03f7ebe5fee -
Trigger Event:
push
-
Statement type:
File details
Details for the file imail_cli-1.3.1-py3-none-any.whl.
File metadata
- Download URL: imail_cli-1.3.1-py3-none-any.whl
- Upload date:
- Size: 73.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
db204e2bd931cf310472b91964ef2bbd8629ceab5a339f1ca64d469c485165e7
|
|
| MD5 |
7047137a05861dae279a0f8f67318307
|
|
| BLAKE2b-256 |
49e42d6d5283ae8b5458df3f5c9b4099f008f122f0a527eae91df25d2f6b3849
|
Provenance
The following attestation bundles were made for imail_cli-1.3.1-py3-none-any.whl:
Publisher:
release.yml on jessecu2024/imail
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
imail_cli-1.3.1-py3-none-any.whl -
Subject digest:
db204e2bd931cf310472b91964ef2bbd8629ceab5a339f1ca64d469c485165e7 - Sigstore transparency entry: 1573239433
- Sigstore integration time:
-
Permalink:
jessecu2024/imail@ca6be9f05b46367ade8c90766c9ff03f7ebe5fee -
Branch / Tag:
refs/tags/v1.3.1 - Owner: https://github.com/jessecu2024
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@ca6be9f05b46367ade8c90766c9ff03f7ebe5fee -
Trigger Event:
push
-
Statement type: