diff --git a/config.example.yaml b/config.example.yaml index 1ab39970..c32be920 100644 --- a/config.example.yaml +++ b/config.example.yaml @@ -100,6 +100,17 @@ sync: enabled: true accounts: [] # Set in config.local.yaml schedule: "*/15 * * * *" + # Plain IMAP mailboxes — for providers the Gmail source can't reach. + # One source per account. Passwords go in config.local.yaml under + # sync.imap.passwords, keyed by username. See docs/sources.md for the + # optional pass that reads mail whose payload is only legible as an image. + # imap: + # enabled: false + # accounts: + # - host: imap.example.net + # username: me@example.net + # label: mailbox # source name becomes imap:mailbox + # schedule: "*/30 * * * *" github: enabled: true schedule: "*/15 * * * *" diff --git a/docs/config.md b/docs/config.md index 74deb176..52c829f9 100644 --- a/docs/config.md +++ b/docs/config.md @@ -218,6 +218,23 @@ Sources pull data from external services on a schedule. See [sources.md](sources | `sync.gmail.schedule` | cron | `*/15 * * * *` | Fetch frequency | | `sync.gmail.keyring_password` | string | - | gog keyring password | +**IMAP-specific** (see [sources.md](sources.md) for the image pass): + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| `sync.imap.enabled` | bool | `false` | Enable IMAP mailboxes | +| `sync.imap.accounts` | list | `[]` | Mailboxes: `host`, `username`, `label`, `port`, `mailbox` | +| `sync.imap.passwords` | dict | `{}` | Password per username — keep in `config.local.yaml` | +| `sync.imap.schedule` | cron | `*/30 * * * *` | Fetch frequency | +| `sync.imap.initial_lookback_days` | int | `1` | `SINCE` window on first run | +| `sync.imap.match.sender_contains` | list | `[]` | Substrings matched against `From:` | +| `sync.imap.match.attachment_contains` | list | `[]` | Substrings matched against an image's Content-ID / filename | +| `sync.imap.match.only_matched` | bool | `false` | Drop everything that did not match | +| `sync.imap.vision.enabled` | bool | `false` | Run a multimodal pass over a matched message's image | +| `sync.imap.vision.model` | string | *(empty)* | Falls back to `memory.fast_model` | +| `sync.imap.vision.prompt` | string | *(empty)* | What to ask about the image — required when vision is enabled | +| `sync.imap.vision.answer_key` | string | *(empty)* | Label the prompt asks the model to emit; empty = first non-empty line | + **GitHub-specific:** | Key | Type | Default | Description | diff --git a/docs/sources.md b/docs/sources.md index f24a0749..a9411d71 100644 --- a/docs/sources.md +++ b/docs/sources.md @@ -72,6 +72,70 @@ A persistent cron job (`inbox-processor`) runs every 15 minutes: - **Two-step fetch:** Search returns metadata only; body + `internalDate` are fetched per-message via `gog gmail get` (up to 5 concurrent) - **Default schedule:** `*/15 * * * *` (every 15 min) +### IMAP +- **Adapter:** `nerve/sources/imap.py` — plain `imaplib`, no CLI or provider API +- **Scope:** Any IMAP mailbox the Gmail source cannot reach (that one goes through the `gog` CLI / Gmail API), e.g. GMX, Fastmail, a company server +- **Instances:** One source per configured account (`imap: