dsh-email 0.10.6 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +83 -7
- package/README.md +63 -8
- package/lib/client.js +2248 -182
- package/lib/config.d.ts +141 -1
- package/lib/config.js +231 -10
- package/lib/index.d.ts +6 -2
- package/lib/index.js +3 -2
- package/lib/mail-client.d.ts +90 -1
- package/lib/mail-client.js +214 -33
- package/lib/oauth2.d.ts +124 -0
- package/lib/oauth2.js +417 -0
- package/lib/runtime.js +16 -3
- package/lib/settings.d.ts +22 -1
- package/lib/settings.js +122 -34
- package/lib/tool-contract.js +1 -1
- package/lib/tools.js +20 -5
- package/lib/web.d.ts +186 -2
- package/lib/web.js +861 -14
- package/package.json +70 -70
package/README.en.md
CHANGED
|
@@ -16,7 +16,7 @@ Pure Node, **cross-platform** (one codebase for Windows / macOS / Linux), no she
|
|
|
16
16
|
|---|---|
|
|
17
17
|
| `email_list` | List the newest mail in a folder (unread filter, pagination, summaries only, no body) |
|
|
18
18
|
| `email_read` | Read one message's full text by uid (HTML auto-converted to plain text, oversized bodies truncated) |
|
|
19
|
-
| `email_search` | Search subject/sender/recipient/CC by keyword (server-side subject/from/to/cc);
|
|
19
|
+
| `email_search` | Search subject/sender/recipient/CC by keyword (server-side subject/from/to/cc; hits are re-checked against the envelopes, so "match-everything" servers such as QQ are rejected); when nothing believable is returned, it falls back to a body scan of the most recent 30 messages by default (including to/cc) |
|
|
20
20
|
| `email_send` | Send mail on your behalf (attachments supported). **Prompts for confirmation before sending by default**, showing recipients, subject and attachment count; only sends after you approve |
|
|
21
21
|
| `email_folders` | List the mailbox folders (INBOX/Sent/Junk/custom…); feed the `path` to other tools |
|
|
22
22
|
| `email_attachment` | Download an attachment by index (saved to the session workspace by default so the model can read it directly; size capped by `maxAttachmentBytes`) |
|
|
@@ -37,6 +37,9 @@ Example:
|
|
|
37
37
|
|
|
38
38
|
### Changelog
|
|
39
39
|
|
|
40
|
+
- **0.11.0 (2026-09-18)**: merge gurio-wine's four settings-page PRs ([#11](https://github.com/STARDUSTLC666/dsh-email/pull/11)–[#14](https://github.com/STARDUSTLC666/dsh-email/pull/14)) with post-review fixes. **Added**: ① visual multi-account card editor (add/edit/delete, rename, set-default, per-account connection test — edits auto-save; no "Save & Apply" button) and server-preset management (`serverPresets`, custom provider endpoints, no credentials); ② OAuth2 device-code login for Outlook / Exchange Online (IMAP and SMTP share one token; automatic refresh; password-auth accounts unaffected); ③ bilingual settings-panel copy that follows the host's Settings → General language in real time; ④ accounts can pin `authKind` (auto / oauth2 / password), giving hybrid or on-premises tenants that still accept app passwords an escape hatch. **Review fixes**: SMTP OAuth2 could never send (nodemailer's `XOAuth2` reads only `accessToken`, never `pass` — confirmed `EAUTH`); saving no longer unconditionally wipes account-level hand-written imap/smtp endpoints (runtime prefers the account's own host; the old behavior silently re-pointed custom-server accounts to presets, and accounts without a provider lost connection info entirely); rename preserves stored auth codes and advanced keys and refuses to overwrite an existing account name; settings routes now enforce Host / Origin / Content-Type same-origin checks (previously any web page could cross-origin-write settings; DNS rebinding could read snapshots containing plaintext auth codes); responses no longer echo the resolved account map (a plaintext-password copy the front end never reads); raw server errors are credential-scrubbed before display (IMAP/SMTP echo rejected auth strings containing access tokens); deleting an account cleans its tokens (uncommitted saves do not); version conflicts auto-rebase instead of retrying with a stale revision. **No third-party OAuth2 app registration is bundled**: OAuth2 accounts must supply their own `clientId` — see "Outlook OAuth2" below. Tests: 81 → 237. **`email_search` fix**: servers like QQ answer any keyword with the same unrelated uid list; hits are now re-verified against the envelopes (subject/from/to/cc) and fall back to the local body scan when none survive, so an impossible keyword no longer "matches" 40 messages ([#15](https://github.com/STARDUSTLC666/dsh-email/issues/15)).
|
|
41
|
+
- **0.10.8 (2026-09-16)**: integrate GUODnuli's [PR #9](https://github.com/STARDUSTLC666/dsh-email/pull/9), replacing nonexistent text and border variables in settings and notifications with official theme tokens; revalidate Harness 0.1.5-rc.2 and 0.1.6-alpha.1.
|
|
42
|
+
- **0.10.7 (2026-09-11)**: revalidate official Harness 0.1.5-rc.1 and refresh suite co-load and live-service evidence; runtime code is unchanged.
|
|
40
43
|
- **0.10.6 (2026-09-10)**: fix an empty authorization-code field shadowing `DSH_EMAIL_PASSWORD` in single-account connection tests and saved settings. Explicit passwords still take precedence; named accounts cannot borrow this environment variable. Refresh the settings tool count, multi-account guidance and real QQ mailbox validation notes.
|
|
41
44
|
- **0.10.5 (2026-09-08)**: document installation, tool registration and the Web settings endpoint in official Harness 0.1.3-alpha.2; update Node requirements and clarify that `email_health` checks configuration only. Runtime code is unchanged from 0.10.4.
|
|
42
45
|
- **0.10.4 (2026-09-07)**: raise the minimum `mailparser` version to `3.9.22` and update the lockfile to use the patched `html-to-text 10.0.1 → deepmerge-ts 8.0.2` dependency chain for [CVE-2026-40345](https://github.com/RebeccaStevens/deepmerge-ts/security/advisories/GHSA-ggr8-5vv4-36mx). This does not rely on root-only `pnpm.overrides`, which cannot fix consumers installing this plugin as a dependency. Add runtime dependency-chain and HTML-message parsing regression tests. An affected dependency is not proof that mail input can trigger this vulnerability.
|
|
@@ -48,7 +51,11 @@ Example:
|
|
|
48
51
|
|
|
49
52
|
## Compatibility
|
|
50
53
|
|
|
51
|
-
|
|
54
|
+
Co-load verification was performed on 2026-09-16 with official source builds of Harness `0.1.5-rc.2` and `0.1.6-alpha.1`: all 18 components load alongside ModLens, with passing tool schemas, skill registration and offline read-only calls.
|
|
55
|
+
|
|
56
|
+
**0.11.0 co-load verification (2026-09-18, locally built Harness `0.1.5-rc.2`, `web` profile)**: the plugin mounted without errors; the settings route answered GET with 200 and no `raw` field in the response; a `text/plain` POST was refused with **415**, proving the same-origin guard holds in the real host; under a `application/json` POST the card projection was correct, and an account pinning `authKind: password` reported both `authKindDeclared` and `authKind` as `password`; the panel actually rendered account cards, the eight provider presets with localized labels, the three-way authentication selector, the application (client) ID field with its hint, the missing-ID warning banner (so `--dsw-alias-state-warn-primary` is genuinely defined in the real host) and the Sign in with Microsoft button; the browser console was clean; save worked end to end, and afterwards `accountsYaml` was restored to empty with the original account intact. Offline tests: 231 green. **Still not done**: an end-to-end OAuth2 run against a real Outlook tenant (the device-code flow needs a human to authorize in a browser) and real sending; the `clientId` paths are covered only against a fake authority. Uses the `cordis.patch.yml` + `dsh.bundle.patch` bundle model. Node requirements are 22.19 or later within 22.x, or 24 or later. Live external-service workflows require separate configuration and validation.
|
|
57
|
+
|
|
58
|
+
On 2026-09-10, npm `dsh-email@0.10.6` passed real QQ mailbox folder/list/read/search calls, the settings page's connection test and Save & Apply, and separate SMTP authentication. An empty authorization-code field correctly used `DSH_EMAIL_PASSWORD`. This recheck did not connect to a real mailbox or send, modify or delete mail.
|
|
52
59
|
|
|
53
60
|
Follows the official [plugin packaging and installation requirements](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/user/develop/basic/publish.md): an ESM entry point, prebuilt `lib/`, `dsh.bundle.patch` and a `cordis.patch.yml` layer. The plugin explicitly injects its required services and supplies JSON Schema parameters, canonical output and rendering, with no runtime imports of `@deepseek-ai/*` internals. Use Node 22.19 or later within 22.x, or Node 24 or later. Harness is evolving rapidly; the version above is the tested baseline.
|
|
54
61
|
|
|
@@ -64,8 +71,12 @@ After installing, restart `dsh web`. The plugin ships with an empty config and *
|
|
|
64
71
|
|
|
65
72
|
**Two configuration methods (pick one):**
|
|
66
73
|
|
|
67
|
-
1. **Web settings (recommended)**: after restart, open **Settings → Mail (dsh-email)**, fill in the email address and authorization code,
|
|
68
|
-
2. **YAML**: hand-write the cordis.patch.yml template below
|
|
74
|
+
1. **Web settings (recommended)**: after restart, open **Settings → Mail (dsh-email)**, fill in the email address and authorization code in an account card — edits auto-save, no button to click; each card also has its own "Test connection" button. Zero YAML, zero restart.
|
|
75
|
+
2. **YAML**: hand-write the `accounts` map in the cordis.patch.yml template below. The settings page's `accountsYaml` is written by the card editor (it overrides `accounts` when non-empty); the panel itself no longer provides a raw-YAML textarea. Fields not modeled by cards (e.g. `socketTimeoutMs`, `connectionTimeoutMs`) can still be hand-written in YAML and are preserved in place when cards are saved. Authentication method (`authKind`) has a selector on the card — no need to hand-write it.
|
|
76
|
+
|
|
77
|
+
The whole settings page follows DSH's light and dark themes: every panel style references the official `--dsw-alias-*` design tokens instead of hard-coded colors, so switching themes takes effect immediately (0.10.8 referenced a border token that does not exist, which produced a glaring light-gray border in dark mode; that is fixed).
|
|
78
|
+
|
|
79
|
+
Multiple accounts can be edited visually in the settings page: account cards add, edit and delete accounts, rename them, pick the default, and run "Test connection" per account name; a half-filled account never blocks saving — it just gets an "incomplete" badge. Card edits are debounced and auto-saved; there is no longer a "write to YAML text, then click save" step. Version conflicts (settings changed elsewhere) are automatically rebased and re-saved once, rather than repeatedly failing with a stale revision. When a card is saved, an already-stored authorization code is kept by default (leave the password field empty to keep it, type into it to overwrite); comments in the YAML are preserved in place where possible — with an explicit notice when they cannot be. Rename re-keys in place, preserving auth codes and advanced keys, and refuses to overwrite an existing account name. Account-level hand-written imap/smtp endpoints are only cleaned when the **provider actually changes** — runtime resolution prefers the account's own host, so a routine save never silently re-points the connection target.
|
|
69
80
|
|
|
70
81
|
Values saved in the settings page live in the `dsh-email` namespace of `settings.yaml` and override the YAML default-account config. Authorization-code fields are marked secret, but saving a filled field still writes its value to the local settings file. For a single account, set `DSH_EMAIL_PASSWORD` and leave the authorization-code field empty to avoid saving it; the environment value is not copied into settings.
|
|
71
82
|
|
|
@@ -116,6 +127,20 @@ Multiple accounts: one `tool-email` line can hold several mailboxes; select one
|
|
|
116
127
|
|
|
117
128
|
Top-level `provider`/`user`/`password`/`imap`/`smtp`/`inboxFolder` remain available as shared defaults for all accounts (the v0.1 single-account style stays fully compatible).
|
|
118
129
|
|
|
130
|
+
To reuse one set of connection endpoints across accounts, define your own provider presets with `serverPresets` (a YAML map: key = preset name, value has an optional `label` plus `imap`/`smtp`):
|
|
131
|
+
|
|
132
|
+
```yaml
|
|
133
|
+
- id: tool-email
|
|
134
|
+
config:
|
|
135
|
+
serverPresets: |
|
|
136
|
+
corp:
|
|
137
|
+
label: 公司邮箱
|
|
138
|
+
imap: { host: imap.corp.example, port: 993, secure: true }
|
|
139
|
+
smtp: { host: smtp.corp.example, port: 465, secure: true }
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
The settings page's "Server presets" fold-out edits these presets visually, and the account cards' provider dropdown automatically gains the preset names — picking one pre-fills its endpoints into the account. A preset holds connection endpoints only — **never an email address or authorization code**. `port`/`secure` may be omitted (defaults are 993/465 with SSL).
|
|
143
|
+
|
|
119
144
|
## Presets
|
|
120
145
|
|
|
121
146
|
| provider | IMAP | SMTP |
|
|
@@ -129,6 +154,30 @@ Top-level `provider`/`user`/`password`/`imap`/`smtp`/`inboxFolder` remain availa
|
|
|
129
154
|
| `outlook` | outlook.office365.com:993 | smtp.office365.com:587 (STARTTLS) |
|
|
130
155
|
| `icloud` | imap.mail.me.com:993 | smtp.mail.me.com:587 (STARTTLS) |
|
|
131
156
|
|
|
157
|
+
### Full configuration options
|
|
158
|
+
|
|
159
|
+
| Field | Default | Description |
|
|
160
|
+
|---|---|---|
|
|
161
|
+
| `provider` | — | Preset name; auto-fills imap/smtp addresses. Explicitly written host/port/secure take precedence |
|
|
162
|
+
| `user` | required | Login email address |
|
|
163
|
+
| `password` | required* | Authorization code / app-specific password; *can also use the env var `DSH_EMAIL_PASSWORD` |
|
|
164
|
+
| `imap.host/port/secure` | per preset | Incoming server (also `connectionTimeoutMs` / `socketTimeoutMs` for timeouts) |
|
|
165
|
+
| `smtp.host/port/secure` | per preset | Outgoing server |
|
|
166
|
+
| `inboxFolder` | `INBOX` | Default folder for read/send tools |
|
|
167
|
+
| `sendApproval` | `true` | Confirm before sending (strongly recommended) |
|
|
168
|
+
| `maxBodyChars` | `20000` | Body truncation limit for `email_read` (1000–200000) |
|
|
169
|
+
| `accounts` | — | Named account map; account-level fields override top-level shorthand |
|
|
170
|
+
| `accountsYaml` | — | YAML text of the account map, written by the settings-page card editor; overrides `accounts` when non-empty |
|
|
171
|
+
| `clientId` | — | Application (client) ID for OAuth2 accounts; account-level overrides top-level shorthand. The plugin bundles no third-party registration — required for Outlook / Exchange Online OAuth2 (see "Outlook OAuth2" below) |
|
|
172
|
+
| `authKind` | derived from provider | Authentication method override: `oauth2` / `password`. When omitted, derived from provider and IMAP host; hybrid or on-premises tenants that still accept app passwords for Exchange Online can pin `password`. The card's "Authentication method" selector writes this key |
|
|
173
|
+
| `serverPresets` | — | YAML text of custom provider presets (key = preset name, value has optional `label` + `imap`/`smtp`); endpoints only, no credentials. The settings-page dropdown lists preset names and pre-fills endpoints into account cards; editing a preset does not reconnect established connections |
|
|
174
|
+
| `defaultAccount` | auto (single account) | Account used when the `account` argument is omitted (required for multi-account) |
|
|
175
|
+
| `downloadDir` | `.dsh-email-downloads` under session workspace (fallback `$DSH_HOME/email-downloads`) | Download directory for `email_attachment`; pinned once explicitly set |
|
|
176
|
+
| `maxAttachmentBytes` | 20 MiB | Per-attachment and total size cap (1024–512 MiB) |
|
|
177
|
+
| `idleTimeoutMs` | `60000` | IMAP idle connection recycle time (connection reuse; consecutive ops are faster) |
|
|
178
|
+
| `bodySearchFallback` | `true` | Fall back to client-side body scan of recent messages when server search returns nothing |
|
|
179
|
+
| `bodySearchLimit` | `30` | Number of messages for the body fallback scan (5–200) |
|
|
180
|
+
|
|
132
181
|
## Getting an authorization code
|
|
133
182
|
|
|
134
183
|
Every provider requires an authorization code / app-specific password instead of your login password:
|
|
@@ -145,12 +194,39 @@ Every provider requires an authorization code / app-specific password instead of
|
|
|
145
194
|
- When the session is in **Full Access** mode, the harness approval policy is never (no confirmation dialogs) — `email_send` is **blocked with a clear hint**. Two ways out: ① switch the access mode back to Read Only / Write; ② turn off `sendApproval` (uncheck "Confirm before sending" in the settings page), explicitly declaring you accept the risk.
|
|
146
195
|
- This plugin performs no outbound telemetry; credentials are used in memory only to connect to your mail servers.
|
|
147
196
|
|
|
197
|
+
## Outlook OAuth2 (device-code login)
|
|
198
|
+
|
|
199
|
+
Microsoft has disabled username+password basic auth for Exchange Online: personal outlook.com accounts and most tenants now require OAuth2. This plugin supports the device-code flow — IMAP and SMTP share a single token with automatic refresh.
|
|
200
|
+
|
|
201
|
+
**Why no bundled application ID**: a client ID is "someone's app registration." If the plugin shipped one, the Microsoft consent screen would show another party's app name (enterprise security teams typically deny it outright), sign-in logs and telemetry would land in that party's tenant (including your UPN), and if they ever deleted the app every user's login would break simultaneously — with only a cryptic "clientId may be wrong" error. Therefore this plugin **carries no third-party registration**; please register your own (free, ~10 minutes).
|
|
202
|
+
|
|
203
|
+
**Registering a public client**:
|
|
204
|
+
|
|
205
|
+
1. Open the [Entra admin center](https://entra.microsoft.com/) → **App registrations** → **New registration**.
|
|
206
|
+
2. Under **Supported account types**, select "Accounts in any organizational directory and personal Microsoft accounts" — this determines whether personal outlook.com accounts can sign in. Choosing incorrectly yields `AADSTS700016` or `AADSTS50020`.
|
|
207
|
+
3. Leave the **Redirect URI** blank (the device-code flow does not need one). Click Register.
|
|
208
|
+
4. On the Overview page, copy the **Application (client) ID** — this is the `clientId` you will supply.
|
|
209
|
+
5. In the left sidebar, go to **Authentication** → scroll to the bottom → set **Allow public client flows** to **Yes** and save. Without this, login fails with an `AADSTS700028`-style "device-code flow not enabled" error.
|
|
210
|
+
6. In the left sidebar, go to **API permissions** → Add a permission → Microsoft Graph → **Delegated permissions** → check `IMAP.AccessAsUser.All`, `SMTP.Send`, and `offline_access` (the last one is essential for obtaining a refresh token — without it, every expiry forces a fresh login). Personal tenants generally need no admin consent; enterprise tenants may require an admin to click "Grant admin consent" once.
|
|
211
|
+
|
|
212
|
+
**Filling it into the plugin**: Settings → Mail (dsh-email) → the account card's "Application (client) ID" field; or in YAML (account-level `clientId`, which can also be set at the top level as a default for all accounts).
|
|
213
|
+
|
|
214
|
+
**Login flow**: click "Sign in to Microsoft account" on the card → the panel shows a `microsoft.com/devicelogin` link and a code → open the link in a browser, enter the code, and complete authorization → the panel polls until it shows "Signed in: your@email". Both receiving and sending then use this token.
|
|
215
|
+
|
|
216
|
+
**Caveats**:
|
|
217
|
+
|
|
218
|
+
- Enterprise tenants may additionally require an admin to enable **IMAP** and **SMTP AUTH** for the mailbox in the Exchange admin center. The typical symptom of SMTP AUTH being off: receiving works fine, sending is rejected.
|
|
219
|
+
- The token is bound to the application ID that issued it: changing `clientId` is treated as "switched apps" and requires re-login (this is intentional — it prevents using the old app's credentials against the new one).
|
|
220
|
+
- If your tenant is hybrid or on-premises and SMTP AUTH is still enabled, app passwords work: select "Password / authorization code" in the card's "Authentication method" selector — no OAuth2 needed.
|
|
221
|
+
|
|
148
222
|
## Known limitations
|
|
149
223
|
|
|
150
|
-
- **
|
|
224
|
+
- **OAuth2 covers Outlook / Exchange Online only, and requires your own app ID**: device-code login supports both IMAP and SMTP, but the plugin **bundles no third-party app registration** — OAuth2 accounts must supply their own `clientId` (free to register; see "Outlook OAuth2" above). Other environments that mandate OAuth (e.g. Google Workspace) remain unusable; use the provider's app-specific password / authorization code instead.
|
|
151
225
|
- **Body search**: the server side only searches subject / from / to / cc. Most servers (e.g. QQ) have unreliable IMAP `TEXT` / `HEADER` search, so with no results it falls back to a body scan of the most recent `bodySearchLimit` messages (slower; disable with `bodySearchFallback`).
|
|
152
226
|
- **Attachments**: inline images aren't downloadable separately yet; a failed attachment match errors instead of downloading the wrong file (safe default).
|
|
153
227
|
- **Password storage**: the authorization code saved in the settings page is written in plaintext to the local `settings.yaml` (the secret mark only keeps it out of logs / exports / diagnostics; no disk encryption). Don't hand `settings.yaml` to untrusted people.
|
|
228
|
+
- **OAuth2 token storage**: access / refresh tokens are stored as plaintext JSON at `$DSH_HOME/data/dsh-email/oauth2-tokens.json` (deliberately kept out of `settings.yaml`, so they are not included in settings exports). The file is written with `mode: 0o600`, but that permission bit only takes effect **at file creation** and is **effectively a no-op on Windows** — do not hand this file to untrusted people. Tokens are bound to the issuing application ID; changing `clientId` requires re-login. Deleting an account cleans up its tokens.
|
|
229
|
+
- **Local edits are undone by `pnpm install`**: if you deploy by editing files inside `node_modules/dsh-email/`, any `pnpm install` restores the registry version (0.10.7, for example). To keep changes long-term, install from a local path or a Git commit instead.
|
|
154
230
|
|
|
155
231
|
## Development
|
|
156
232
|
|
|
@@ -160,9 +236,9 @@ pnpm run build # tsc → lib/
|
|
|
160
236
|
pnpm test # build + offline tests; no real mailbox required
|
|
161
237
|
```
|
|
162
238
|
|
|
163
|
-
`src/index.ts` composes the plugin. `runtime.ts` owns live settings, account pools, and separate tool/web watch cursors. `tools.ts` wires the ten tool implementations. `tool-contract.ts` defines parameters, output schemas, and text rendering. `approval.ts` owns the outgoing-mail gate. IMAP/SMTP transport remains in `mail-client.ts`, and browser routes remain in `web.ts
|
|
239
|
+
`src/index.ts` composes the plugin. `runtime.ts` owns live settings, account pools, and separate tool/web watch cursors. `tools.ts` wires the ten tool implementations. `tool-contract.ts` defines parameters, output schemas, and text rendering. `approval.ts` owns the outgoing-mail gate. IMAP/SMTP transport remains in `mail-client.ts`, and browser routes remain in `web.ts` — which also hosts the parsing, serialization (comment-preserving, keeping stored authorization codes) and custom-preset snapshot the account-card editor depends on.
|
|
164
240
|
|
|
165
|
-
Tests cover pool replacement after live settings changes, unload cleanup, cancellation and workspace propagation, independent tool/web cursors, and rejected approval preventing send execution. In-memory clients replace mailbox connections.
|
|
241
|
+
Tests cover pool replacement after live settings changes, unload cleanup, cancellation and workspace propagation, independent tool/web cursors, account-card serialization and preset parsing, and rejected approval preventing send execution. In-memory clients replace mailbox connections.
|
|
166
242
|
|
|
167
243
|
## License
|
|
168
244
|
|
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ IMAP/SMTP email tools for DeepSeek Harness, with replies, forwarding, mailbox or
|
|
|
24
24
|
|---|---|
|
|
25
25
|
| `email_list` | 列出文件夹里最新的邮件(未读过滤、分页、只看摘要不带正文) |
|
|
26
26
|
| `email_read` | 按 uid 读取一封邮件的全文(HTML 邮件自动转纯文本,超长截断) |
|
|
27
|
-
| `email_search` | 按关键词搜索主题/发件人/收件人/抄送(服务器端 subject/from/to/cc
|
|
27
|
+
| `email_search` | 按关键词搜索主题/发件人/收件人/抄送(服务器端 subject/from/to/cc;命中会先用信封复核,QQ 这种"什么都匹配"的响应会被判无效);复核或服务器都没给出可信结果时,默认回退到最近 30 封的正文扫描(含 to/cc) |
|
|
28
28
|
| `email_send` | 代发邮件(支持带附件)。**默认发信前会弹确认**,显示收件人、主题和附件数,由你批准后才发出 |
|
|
29
29
|
| `email_folders` | 列出邮箱的文件夹(INBOX/已发送/垃圾邮件/自定义…),拿 path 喂给其他工具 |
|
|
30
30
|
| `email_attachment` | 按序号下载邮件附件(默认存到会话工作区,模型可直接读取;大小受 maxAttachmentBytes 限制) |
|
|
@@ -45,6 +45,9 @@ IMAP/SMTP email tools for DeepSeek Harness, with replies, forwarding, mailbox or
|
|
|
45
45
|
|
|
46
46
|
### 版本记录
|
|
47
47
|
|
|
48
|
+
- **0.11.0(2026-09-18)**:合入 gurio-wine 的设置页四连([PR #11](https://github.com/STARDUSTLC666/dsh-email/pull/11)–[#14](https://github.com/STARDUSTLC666/dsh-email/pull/14)),并在评审后修掉其中若干问题。**新增**:①多账号卡片编辑器(增删改 / 改名 / 设默认 / 按账号单独测试连接,编辑即保存,不再需要点「保存并应用」)与服务器预设管理(`serverPresets`,自定义服务商端点,不含凭证);②Outlook / Exchange Online 的 OAuth2 设备码登录(IMAP 与 SMTP 双端,access token 自动刷新,密码认证账号完全不受影响);③设置面板文案中英双语,跟随宿主 Settings → General 的语言实时切换;④账号可显式钉住 `authKind`(自动 / oauth2 / password),给仍能用应用密码连 Exchange Online 的混合或本地租户留退路。**评审修复**:SMTP 的 OAuth2 认证形状原本一封也发不出去(nodemailer 的 `XOAuth2` 只读 `accessToken`、从不读 `pass`,实测报 `EAUTH`);保存面板不再无条件抹掉账号手写的 imap/smtp 端点(运行时解析以账号自己的值优先,原行为会把自建服务器账号静默改指预设,无 provider 的账号则直接失去连接信息);改名保留授权码与高级键,且不允许顶掉同名账号;设置路由增加 Host / Origin / Content-Type 同源校验(此前任意网页都能跨源改设置,DNS rebinding 还能读走含明文授权码的快照);响应不再回显解析后的账号映射(那是一份含明文密码、前端从不读取的副本);服务器原始报错经凭据脱敏后才展示(IMAP/SMTP 会回显被拒的认证串,其中含 access token);删除账号即清理其 token,未提交的保存不清;版本冲突自动重基,而不是拿旧 revision 反复重试。**不内置任何第三方 OAuth2 应用注册**:OAuth2 账号需自带 `clientId`,见下文「Outlook OAuth2」。测试 81 → 237 项。**修复 `email_search`**:QQ 这类服务器会对任意关键词返回同一批无关 UID,现在服务器命中会先用 envelope 复核(subject/from/to/cc),核实不到就回退本地正文扫描,不会再出现「不存在的关键词也匹配 40 条」([#15](https://github.com/STARDUSTLC666/dsh-email/issues/15))。
|
|
49
|
+
- **0.10.8(2026-09-16)**:合入 GUODnuli 的 [PR #9](https://github.com/STARDUSTLC666/dsh-email/pull/9),将设置页及新邮件弹窗的文字、边框引用改为官方主题变量,修复深色主题文字不可读;复验官方 Harness 0.1.5-rc.2 和 0.1.6-alpha.1。
|
|
50
|
+
- **0.10.7(2026-09-11)**:复验官方 Harness 0.1.5-rc.1,更新整套同载与真实服务验证记录;运行时代码未变。
|
|
48
51
|
- **0.10.6(2026-09-10)**:修复单账号设置页授权码留空时,空字符串遮蔽 `DSH_EMAIL_PASSWORD`,导致“测试连接”和保存后工具调用报未配置的问题;显式密码仍优先,多账号不会借用该环境变量。更新设置页工具数量、多账号说明,并补充真实 QQ 邮箱验证结果。
|
|
49
52
|
- **0.10.5(2026-09-08)**:补充官方 Harness 0.1.3-alpha.2 的安装、工具注册及 Web 设置接口验证,更新 Node 版本要求,明确 `email_health` 只检查配置;运行时代码与 0.10.4 相同。
|
|
50
53
|
- **0.10.4(2026-09-07)**:将 `mailparser` 最低版本提升到 `3.9.22` 并更新锁文件,使用 `html-to-text 10.0.1 → deepmerge-ts 8.0.2` 的修复链处理 [CVE-2026-40345](https://github.com/RebeccaStevens/deepmerge-ts/security/advisories/GHSA-ggr8-5vv4-36mx)。不依赖插件作为下游依赖安装时不生效的根级 `pnpm.overrides`;新增真实依赖链与 HTML 邮件解析回归测试。依赖告警不等于已证实邮件输入可触发该漏洞。
|
|
@@ -59,7 +62,11 @@ IMAP/SMTP email tools for DeepSeek Harness, with replies, forwarding, mailbox or
|
|
|
59
62
|
|
|
60
63
|
## 兼容性
|
|
61
64
|
|
|
62
|
-
|
|
65
|
+
2026-09-16 曾在官方源码构建的 Harness `0.1.5-rc.2` 和 `0.1.6-alpha.1` 上完成同载验证:18 个组件与 ModLens 同载,工具 schema、技能注册及离线只读调用检查通过。
|
|
66
|
+
|
|
67
|
+
**0.11.0 的同载验证(2026-09-18,本地构建的 Harness `0.1.5-rc.2`,`web` profile)**:插件挂载无报错;设置路由 GET 返回 200 且响应中已无 `raw` 字段;用 `text/plain` 发 POST 被 **415** 拒绝(同源守卫在真实宿主下生效);`application/json` 的 POST 下卡片投影正确,账号钉住 `authKind: password` 后 `authKindDeclared` 与 `authKind` 均为 `password`;设置面板实际渲染出账号卡片、8 个服务商预设的中文下拉、「认证方式」三态选择器、「应用(客户端)ID」输入格与提示、未填 ID 时的警示条(说明 `--dsw-alias-state-warn-primary` 在真实宿主下确有定义)与「登录 Microsoft 账号」按钮;浏览器控制台无报错;save 全链路可用,验证结束后已把 `accountsYaml` 还原为空、原有账号恢复。离线测试 231 项全绿。**仍未做**:真实 Outlook 租户的 OAuth2 端到端(设备码流程要真人在浏览器完成授权)与真实发信未测,`clientId` 相关路径目前只有假 authority 的用例覆盖。采用 `cordis.patch.yml` + `dsh.bundle.patch` 组合包模型。Node 要求为 22.19 及以上的 22.x,或 24 及以上。外部服务的实际业务操作需按各组件配置单独验证。
|
|
68
|
+
|
|
69
|
+
2026-09-10,npm `dsh-email@0.10.6` 曾通过真实 QQ 邮箱目录、列表、读取和搜索,以及设置页“测试连接”“保存并应用”检查;授权码留空时能继续使用 `DSH_EMAIL_PASSWORD`。独立 SMTP 登录认证也已通过。此次复验未连接真实邮箱,未发送、修改或删除邮件。
|
|
63
70
|
|
|
64
71
|
遵循官方[插件打包与安装要求](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/user/develop/basic/publish.md):ESM 入口、预构建 `lib/`、`dsh.bundle.patch` 和 `cordis.patch.yml` 配置层;显式注入所需服务,提供 JSON Schema 参数、规范化输出和渲染函数,运行时不 import `@deepseek-ai/*` 内部模块。使用 Node 22.19 及以上的 22.x 或 Node 24 及以上版本;Harness 仍在快速迭代,上述版本是实测基线。
|
|
65
72
|
|
|
@@ -75,8 +82,12 @@ dsh plugin --profile web add dsh-email
|
|
|
75
82
|
|
|
76
83
|
**配置方式有两种(任选其一):**
|
|
77
84
|
|
|
78
|
-
1. **网页设置(推荐)**:重启后打开 **设置 → 邮件 (dsh-email)
|
|
79
|
-
2. **YAML**:按下面的 cordis.patch.yml
|
|
85
|
+
1. **网页设置(推荐)**:重启后打开 **设置 → 邮件 (dsh-email)**,在账号卡片里填邮箱地址和授权码——改动即自动保存,无需再点按钮;每张卡片还能单独「测试连接」。零 YAML、零重启。
|
|
86
|
+
2. **YAML**:按下面的 cordis.patch.yml 模板手写 `accounts` 映射。设置页的 `accountsYaml` 由卡片编辑器写入(非空时覆盖 `accounts`),面板本身不再提供 YAML 原文文本框;卡片不建模的字段(如 `socketTimeoutMs`、`connectionTimeoutMs`)仍可在 YAML 里手写,保存卡片时会原地保留。认证方式(`authKind`)已在卡片上提供选择器,无需手写。
|
|
87
|
+
|
|
88
|
+
设置页整体跟随 DSH 的深浅主题:面板样式全部引用官方 `--dsw-alias-*` 设计变量、不写死颜色,切换浅色/深色即时生效(0.10.8 曾引用一个并不存在的边框变量,暗色下会出现刺眼的浅灰边框,已修掉)。
|
|
89
|
+
|
|
90
|
+
多账号可以在设置页可视化编辑:账号卡片支持增删改账号、改名、设默认、按账号名单独「测试连接」;没填完的账号不阻断保存,只标一个「未完成」。卡片改动即时防抖落盘,不再有"先写 YAML 文本、再点一次保存"这一步;版本冲突(别处也改了设置)会自动重基后重存一次,而不是拿旧版本号反复失败。保存卡片时,已存的授权码默认保持(密码栏留空 = 不变,填内容 = 覆盖);YAML 里的注释尽量原地保留,实在保不住时会明确提示。改名走的是原地改键,授权码与高级键一并保留,且不允许改成已有账号名(那会顶掉另一个账号)。账号自己手写的 imap/smtp 端点只在**服务商真的换了**时才清洗——运行时以账号自己的 host 优先,普通保存不会悄悄改动连接目标。
|
|
80
91
|
|
|
81
92
|
设置页保存的值存在 `settings.yaml` 的 `dsh-email` 命名空间里,覆盖 YAML 的默认账号配置。授权码字段标记为 secret,但填写后保存仍会写入本机配置文件。单账号如需避免保存授权码,可设置 `DSH_EMAIL_PASSWORD` 并将授权码栏留空;环境变量不会被复制进设置文件。
|
|
82
93
|
|
|
@@ -127,6 +138,20 @@ dsh plugin --profile web remove dsh-email
|
|
|
127
138
|
|
|
128
139
|
顶层的 `provider`/`user`/`password`/`imap`/`smtp`/`inboxFolder` 仍然可用,作为各账号的共享默认值(v0.1 单账号写法完全兼容)。
|
|
129
140
|
|
|
141
|
+
想在多个账号之间复用同一套连接端点,可以用 `serverPresets` 自定义服务商预设(YAML 映射,键 = 预设名,值含可选的 `label` 与 `imap`/`smtp`):
|
|
142
|
+
|
|
143
|
+
```yaml
|
|
144
|
+
- id: tool-email
|
|
145
|
+
config:
|
|
146
|
+
serverPresets: |
|
|
147
|
+
corp:
|
|
148
|
+
label: 公司邮箱
|
|
149
|
+
imap: { host: imap.corp.example, port: 993, secure: true }
|
|
150
|
+
smtp: { host: smtp.corp.example, port: 465, secure: true }
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
设置页的「服务器预设」折叠区能可视化增删改这些预设,账号卡片的服务商下拉里会自动多出预设名,选中即把端点预填进账号。预设只记连接参数,**不含邮箱地址和授权码**;`port`/`secure` 可省略(默认 993/465 与 SSL)。
|
|
154
|
+
|
|
130
155
|
### 常用邮箱预设
|
|
131
156
|
|
|
132
157
|
| provider | IMAP | SMTP |
|
|
@@ -153,7 +178,10 @@ dsh plugin --profile web remove dsh-email
|
|
|
153
178
|
| `sendApproval` | `true` | 发信前弹确认(强烈建议保留) |
|
|
154
179
|
| `maxBodyChars` | `20000` | email_read 正文截断上限(1000–200000) |
|
|
155
180
|
| `accounts` | 无 | 具名账号表;账号级字段覆盖顶层简写 |
|
|
156
|
-
| `accountsYaml` | 无 |
|
|
181
|
+
| `accountsYaml` | 无 | 账号映射的 YAML 文本,由设置页的卡片编辑器写入;非空时覆盖 accounts |
|
|
182
|
+
| `clientId` | 无 | OAuth2 账号的应用(客户端)ID;账号级可覆盖顶层简写。插件不内置任何第三方注册,Outlook / Exchange Online 走 OAuth2 时必填(见「Outlook OAuth2」) |
|
|
183
|
+
| `authKind` | 按 provider 派生 | 认证方式覆盖,取值 `oauth2` / `password`。缺省时按 provider 与 IMAP 主机派生;仍能用应用密码连 Exchange Online 的混合或本地租户可钉 `password`。设置页卡片的「认证方式」选择器即写此键 |
|
|
184
|
+
| `serverPresets` | 无 | 自定义服务商预设的 YAML 文本(键=预设名,值含 `label?`/`imap`/`smtp`);只存端点、不含凭证,设置页下拉会列出预设名并把端点预填进账号卡片,改预设不会重连已建立的连接 |
|
|
157
185
|
| `defaultAccount` | 单账号时自动 | 工具省略 account 参数时使用的账号(多账号必填) |
|
|
158
186
|
| `downloadDir` | 会话工作区下 .dsh-email-downloads(回退 $DSH_HOME/email-downloads) | email_attachment 的落盘目录;显式设置后固定 |
|
|
159
187
|
| `maxAttachmentBytes` | 20 MiB | 单个附件与附件总大小上限(1024–512 MiB) |
|
|
@@ -177,12 +205,39 @@ dsh plugin --profile web remove dsh-email
|
|
|
177
205
|
- 会话处于 **Full Access(完全访问)** 模式时,harness 的审批策略是 never(不弹任何确认框)——`email_send` 会**被拦截并给出明确提示**。两条出路:① 把访问模式切回 Read Only / Write;② 关闭 `sendApproval`(设置页勾掉「发信前确认」),即显式声明自行承担风险。
|
|
178
206
|
- 本插件不做任何联网上报,凭证只在内存中用于连接你的邮箱服务器。
|
|
179
207
|
|
|
208
|
+
## Outlook OAuth2(设备码登录)
|
|
209
|
+
|
|
210
|
+
微软已经对 Exchange Online 关闭了用户名+密码的 basic auth:个人 outlook.com 与绝大多数租户现在只能用 OAuth2。本插件支持设备码(device code)流程,IMAP 与 SMTP 双端共用同一份 token,过期自动刷新。
|
|
211
|
+
|
|
212
|
+
**为什么不内置一个应用 ID**:客户端 ID 是"某个人的应用注册"。如果插件自带一个,微软同意屏上显示的会是别人的应用名(企业安全团队通常直接拒授权),登录日志与 telemetry 会归到对方租户(含你的 UPN),而对方哪天删掉这个应用,所有用户的登录会同时失败——报出来的还只是一句"clientId 可能填错了"。所以本插件**不携带任何第三方注册**,请用自己的(免费,约 10 分钟)。
|
|
213
|
+
|
|
214
|
+
**注册一个公共客户端**:
|
|
215
|
+
|
|
216
|
+
1. 打开 [Entra 管理中心](https://entra.microsoft.com/) → **应用注册(App registrations)** → **新注册**。
|
|
217
|
+
2. **受支持的账户类型**选「任何组织目录中的账户 **以及** 个人 Microsoft 账户」——这一项决定了个人 outlook.com 能不能登录,选错会报 `AADSTS700016` 或 `AADSTS50020`。
|
|
218
|
+
3. **重定向 URI** 留空(设备码流程不需要)。点注册。
|
|
219
|
+
4. 在概览页复制 **应用程序(客户端) ID**,这就是要填的 `clientId`。
|
|
220
|
+
5. 左侧 **身份验证** → 页面最下方 **允许公共客户端流** 设为 **是** 并保存。不开这一项,登录会报 `AADSTS700028` 之类的"未开启设备码流"错误。
|
|
221
|
+
6. 左侧 **API 权限** → 添加权限 → Microsoft Graph → **委托的权限**,勾上 `IMAP.AccessAsUser.All`、`SMTP.Send`、`offline_access`(最后这个是拿到 refresh token 的关键,少了它每次过期都要重新登录)。个人租户一般无需管理员同意;企业租户可能需要管理员点一次「授予同意」。
|
|
222
|
+
|
|
223
|
+
**填进插件**:设置页 → 邮件 (dsh-email) → 该账号卡片的「应用(客户端)ID」栏;或者写在 YAML 里(账号级 `clientId`,也可写在顶层作为所有账号的默认)。
|
|
224
|
+
|
|
225
|
+
**登录**:卡片上点「登录 Microsoft 账号」→ 面板给出一个 `microsoft.com/devicelogin` 链接和一段代码 → 在浏览器打开链接、输入代码、完成授权 → 面板轮询到成功后即显示「已登录:你的地址」。之后收信与发信都用这份 token。
|
|
226
|
+
|
|
227
|
+
**注意事项**:
|
|
228
|
+
|
|
229
|
+
- 企业租户可能还需要管理员在 Exchange 管理中心开启该邮箱的 **IMAP** 与 **SMTP AUTH**。没开 SMTP AUTH 时的典型症状是:收信一切正常,发信被拒。
|
|
230
|
+
- token 与签发它的应用 ID 绑定:换了 `clientId` 会被判为"换了应用",需要重新登录(这是有意的,避免拿旧应用的凭据去撞新应用)。
|
|
231
|
+
- 如果你的租户是混合或本地部署、SMTP AUTH 仍然开着,用应用密码也能连:在卡片的「认证方式」里选「密码 / 授权码」即可,不必走 OAuth2。
|
|
232
|
+
|
|
180
233
|
## 已知限制
|
|
181
234
|
|
|
182
|
-
-
|
|
235
|
+
- **OAuth2 仅覆盖 Outlook / Exchange Online,且需自带应用 ID**:设备码登录已支持 IMAP 与 SMTP 双端,但插件**不内置任何第三方应用注册**,OAuth2 账号必须填自己的 `clientId`(免费注册,见上文「Outlook OAuth2」)。Google Workspace 等其它强制 OAuth 的环境仍不可用,只能用服务商的应用专用密码 / 授权码。
|
|
183
236
|
- **正文搜索**:服务器端只搜 subject / from / to / cc;多数服务器(如 QQ)的 IMAP `TEXT` / `HEADER` 搜索不可靠,无结果时回退到最近 `bodySearchLimit` 封的正文扫描(较慢,可用 `bodySearchFallback` 关闭)。
|
|
184
237
|
- **附件**:内嵌图片暂不支持单独下载;附件定位失败会直接报错而不是下载错误文件(安全默认)。
|
|
185
238
|
- **密码落盘**:设置页保存的授权码以明文写在本机 `settings.yaml`(secret 标记只保证它不进日志 / 导出 / 诊断,不做磁盘加密)。请勿把 `settings.yaml` 交给不信任的人。
|
|
239
|
+
- **OAuth2 token 落盘**:access / refresh token 以明文 JSON 存在 `$DSH_HOME/data/dsh-email/oauth2-tokens.json`(刻意不放进 `settings.yaml`,因此不会随设置导出)。写入时带了 `mode: 0o600`,但这个权限位只在**文件创建那一刻**生效,且**在 Windows 上等于无效**——请勿把该文件交给不信任的人。token 与签发它的应用 ID 绑定,换了 `clientId` 需要重新登录;删除账号会清理它的 token。
|
|
240
|
+
- **本地改动会被 `pnpm install` 还原**:如果你是直接改 `node_modules/dsh-email/` 里的文件做本地部署,任何一次 `pnpm install` 都会把它还原成 registry 上的版本(例如 0.10.7);要长期保留请改成从本地路径或 Git 提交安装。
|
|
186
241
|
|
|
187
242
|
## 开发
|
|
188
243
|
|
|
@@ -192,9 +247,9 @@ pnpm run build # tsc → lib/
|
|
|
192
247
|
pnpm test # 构建 + 离线测试,无需真实邮箱
|
|
193
248
|
```
|
|
194
249
|
|
|
195
|
-
`src/index.ts` 只负责组合插件。`runtime.ts` 管理动态设置、账号连接池和网页/工具各自的监视游标;`tools.ts` 接线十个工具的执行逻辑;`tool-contract.ts` 集中维护参数、输出 schema 和中文渲染;`approval.ts` 管理发信审批。IMAP/SMTP 传输仍由 `mail-client.ts` 负责,网页路由由 `web.ts`
|
|
250
|
+
`src/index.ts` 只负责组合插件。`runtime.ts` 管理动态设置、账号连接池和网页/工具各自的监视游标;`tools.ts` 接线十个工具的执行逻辑;`tool-contract.ts` 集中维护参数、输出 schema 和中文渲染;`approval.ts` 管理发信审批。IMAP/SMTP 传输仍由 `mail-client.ts` 负责,网页路由由 `web.ts` 负责——设置页的账号卡片编辑器要的解析、序列化(保留注释、保住已存授权码)和自定义预设快照也在这里。
|
|
196
251
|
|
|
197
|
-
|
|
252
|
+
测试覆盖动态配置换池、卸载释放、取消信号与工作区透传、工具/网页游标隔离、账号卡片序列化与预设解析,以及审批拒绝时不会进入发送执行。测试用内存客户端替代邮箱连接。
|
|
198
253
|
|
|
199
254
|
## 协议
|
|
200
255
|
|