dsh-email 0.10.2 → 0.10.4
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 +11 -3
- package/README.md +11 -5
- package/lib/approval.d.ts +34 -0
- package/lib/approval.js +55 -0
- package/lib/index.d.ts +8 -12
- package/lib/index.js +17 -669
- package/lib/mail-client.d.ts +12 -10
- package/lib/mail-client.js +141 -52
- package/lib/runtime.d.ts +36 -0
- package/lib/runtime.js +92 -0
- package/lib/tool-contract.d.ts +423 -0
- package/lib/tool-contract.js +353 -0
- package/lib/tools.d.ts +13 -0
- package/lib/tools.js +191 -0
- package/package.json +5 -4
package/README.en.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|

|
|
8
8
|
|
|
9
|
-
Email
|
|
9
|
+
Email plugin for DeepSeek Harness: **10 IMAP/SMTP tools** cover reading and searching mail, sending, replies and forwarding, attachments, message flags and moves, incremental new-mail checks, and connection health. Supports multiple accounts, send approval, Web settings and new-mail popups. Configure an account with presets for QQ / 163 / 126 / Sina / Aliyun / Gmail / Outlook / iCloud to get started.
|
|
10
10
|
|
|
11
11
|
Pure Node, **cross-platform** (one codebase for Windows / macOS / Linux), no shell, no native binaries.
|
|
12
12
|
|
|
@@ -20,7 +20,10 @@ Pure Node, **cross-platform** (one codebase for Windows / macOS / Linux), no she
|
|
|
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`) |
|
|
23
|
+
| `email_health` | Check account configuration and IMAP/SMTP connectivity to diagnose connection problems |
|
|
23
24
|
| `email_watch` | Incremental new-mail check: the first call seeds a baseline, every later call reports only unread mail newer than the last check — ideal for scheduled new-mail notifications |
|
|
25
|
+
| `email_mark` | Mark messages as read/unread, add/remove stars, or move messages to another folder |
|
|
26
|
+
| `email_reply` | Reply, reply-all or forward with thread headers and quoted content; uses the same send-approval gate |
|
|
24
27
|
|
|
25
28
|
### New-mail notifications (web UI)
|
|
26
29
|
|
|
@@ -34,6 +37,7 @@ Example:
|
|
|
34
37
|
|
|
35
38
|
### Changelog
|
|
36
39
|
|
|
40
|
+
- **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.
|
|
37
41
|
- **0.9.0**: new `email_watch` incremental new-mail tool (cursor-based, ideal for scheduled notifications); new "whale-girl courier" new-mail popup in the web UI (local skin artwork read at runtime + built-in fallback).
|
|
38
42
|
- **0.8.2**: `since` / `until` parameter descriptions unified to English, consistent with the other parameters, so multilingual agents read them correctly.
|
|
39
43
|
- **0.8.0/0.8.1**: `email_list` / `email_search` gained `since` / `until` date-range filters; new `email_health` self-check (account/connection/config in one call); adapted to harness 0.1.2 (removed the deleted client-injection declaration).
|
|
@@ -42,7 +46,7 @@ Example:
|
|
|
42
46
|
|
|
43
47
|
## Compatibility
|
|
44
48
|
|
|
45
|
-
|
|
49
|
+
Plugin contracts and Web-profile co-loading verified against the official source-run `@deepseek-ai/dsh@0.1.3-alpha.1` baseline on 2026-09-07; this does not claim real-mailbox send/receive verification. Built for the cordis patch-bundle plugin model (`cordis.patch.yml` + `dsh.bundle.patch`). No runtime imports of `@deepseek-ai/*` internals.
|
|
46
50
|
|
|
47
51
|
## Installation
|
|
48
52
|
|
|
@@ -149,9 +153,13 @@ Every provider requires an authorization code / app-specific password instead of
|
|
|
149
153
|
```sh
|
|
150
154
|
pnpm install
|
|
151
155
|
pnpm run build # tsc → lib/
|
|
152
|
-
pnpm test #
|
|
156
|
+
pnpm test # build + offline tests; no real mailbox required
|
|
153
157
|
```
|
|
154
158
|
|
|
159
|
+
`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`.
|
|
160
|
+
|
|
161
|
+
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.
|
|
162
|
+
|
|
155
163
|
## License
|
|
156
164
|
|
|
157
165
|
MIT. This is a community plugin, not affiliated with DeepSeek; `@deepseek-ai/*` is an officially reserved namespace.
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# dsh-email
|
|
4
4
|
|
|
5
|
-
>
|
|
5
|
+
> **让 agent 协助处理邮件**:收发、搜索、回复转发、附件、邮件整理与新邮件提醒,支持八种常见邮箱服务预设。
|
|
6
6
|
|
|
7
7
|
   
|
|
8
8
|
|
|
@@ -12,9 +12,9 @@
|
|
|
12
12
|

|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
DeepSeek Harness
|
|
15
|
+
DeepSeek Harness 邮件插件:通过标准 IMAP/SMTP 提供 **10 个工具**,覆盖邮件查收与搜索、发送与回复转发、附件处理、标记与移动、增量收件和健康检查。支持多个账号、发信审批、Web 设置页与新邮件弹窗;配置邮箱账号后即可使用。
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
IMAP/SMTP email tools for DeepSeek Harness, with replies, forwarding, mailbox organization and new-mail notifications. Presets: QQ / 163 / 126 / Sina / Aliyun / Gmail / Outlook / iCloud.
|
|
18
18
|
|
|
19
19
|
纯 Node 实现,**全平台通用**(Windows / macOS / Linux 同一份代码),不依赖 shell、无原生二进制。
|
|
20
20
|
|
|
@@ -28,6 +28,7 @@ Email tools for DeepSeek Harness: list, read, search and send mail through stand
|
|
|
28
28
|
| `email_send` | 代发邮件(支持带附件)。**默认发信前会弹确认**,显示收件人、主题和附件数,由你批准后才发出 |
|
|
29
29
|
| `email_folders` | 列出邮箱的文件夹(INBOX/已发送/垃圾邮件/自定义…),拿 path 喂给其他工具 |
|
|
30
30
|
| `email_attachment` | 按序号下载邮件附件(默认存到会话工作区,模型可直接读取;大小受 maxAttachmentBytes 限制) |
|
|
31
|
+
| `email_health` | 检查账号配置及 IMAP/SMTP 连接,定位邮箱连接问题 |
|
|
31
32
|
| `email_watch` | 增量检查新邮件:首次调用建立基线,之后每次只报告比上次多出来的未读邮件,适合定时任务做新邮件提醒 |
|
|
32
33
|
| `email_mark` | 修改邮件状态:标记已读/未读、加/取消星标,或移动到别的文件夹(归档、丢回收站),收发闭环的「收完之后」那一半 |
|
|
33
34
|
| `email_reply` | 回复/回复全部/转发已有邮件:自动带上 In-Reply-To/References 线程头与原文引文,收件人自动排除自己,主题不重复叠 Re:/Fwd:;同样走发信审批门 |
|
|
@@ -44,6 +45,7 @@ Email tools for DeepSeek Harness: list, read, search and send mail through stand
|
|
|
44
45
|
|
|
45
46
|
### 版本记录
|
|
46
47
|
|
|
48
|
+
- **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 邮件解析回归测试。依赖告警不等于已证实邮件输入可触发该漏洞。
|
|
47
49
|
- **0.10.1**:补发制品——已发布的 0.10.0 打包时只含 `email_mark`,本版同时包含 `email_mark` 与 `email_reply`,代码与 0.10.0 的 main 一致。
|
|
48
50
|
- **0.10.0**:新增 `email_mark`(已读/未读/星标/移动文件夹,补齐收发闭环的整理侧)与 `email_reply`(回复/回复全部/转发,自动线程头+引文,走发信审批门);连接池按读/写模式分别管理邮箱打开状态。
|
|
49
51
|
- **0.9.1**:修复设置页空主机遮蔽 provider 预设(#3/#6);IMAP 连接超时不再杀死整个 DSH 进程(#4);暗色模式输入控件可见(#2);密码栏提示环境变量 `DSH_EMAIL_PASSWORD` 免明文方案(#5)。
|
|
@@ -55,7 +57,7 @@ Email tools for DeepSeek Harness: list, read, search and send mail through stand
|
|
|
55
57
|
|
|
56
58
|
## 兼容性
|
|
57
59
|
|
|
58
|
-
|
|
60
|
+
已在 `@deepseek-ai/dsh@0.1.3-alpha.1` 官方源码基线上验证插件接口与 Web profile 同载(2026-09-07;不代表已实测真实邮箱收发)。遵循 cordis 组合包补丁模型(`cordis.patch.yml` + `dsh.bundle.patch`),运行时不 import 任何 `@deepseek-ai/*` 内部模块。
|
|
59
61
|
|
|
60
62
|
## 安装
|
|
61
63
|
|
|
@@ -183,9 +185,13 @@ dsh plugin --profile web remove dsh-email
|
|
|
183
185
|
```sh
|
|
184
186
|
pnpm install
|
|
185
187
|
pnpm run build # tsc → lib/
|
|
186
|
-
pnpm test # 构建 +
|
|
188
|
+
pnpm test # 构建 + 离线测试,无需真实邮箱
|
|
187
189
|
```
|
|
188
190
|
|
|
191
|
+
`src/index.ts` 只负责组合插件。`runtime.ts` 管理动态设置、账号连接池和网页/工具各自的监视游标;`tools.ts` 接线十个工具的执行逻辑;`tool-contract.ts` 集中维护参数、输出 schema 和中文渲染;`approval.ts` 管理发信审批。IMAP/SMTP 传输仍由 `mail-client.ts` 负责,网页路由由 `web.ts` 负责。
|
|
192
|
+
|
|
193
|
+
测试覆盖动态配置换池、卸载释放、取消信号与工作区透传、工具/网页游标隔离,以及审批拒绝时不会进入发送执行。测试用内存客户端替代邮箱连接。
|
|
194
|
+
|
|
189
195
|
## 协议
|
|
190
196
|
|
|
191
197
|
MIT。这是一个社区插件,与 DeepSeek 官方无关;`@deepseek-ai/*` 为官方保留命名空间。
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/** Outgoing-mail approval is independent of tool execution. */
|
|
2
|
+
import type { EmailRuntime } from './runtime.js';
|
|
3
|
+
type ApprovalDecision = {
|
|
4
|
+
kind: 'allow';
|
|
5
|
+
} | {
|
|
6
|
+
kind: 'deny';
|
|
7
|
+
reason: string;
|
|
8
|
+
} | {
|
|
9
|
+
kind: 'ask';
|
|
10
|
+
reason?: string;
|
|
11
|
+
};
|
|
12
|
+
interface PendingExecution {
|
|
13
|
+
name: string;
|
|
14
|
+
arguments?: unknown;
|
|
15
|
+
agent?: unknown;
|
|
16
|
+
callId?: string;
|
|
17
|
+
signal?: AbortSignal;
|
|
18
|
+
}
|
|
19
|
+
interface ApprovalContext {
|
|
20
|
+
on(event: 'tools/pre-execute', listener: (exec: PendingExecution, next: () => Promise<ApprovalDecision>) => Promise<ApprovalDecision>, options: {
|
|
21
|
+
prepend: boolean;
|
|
22
|
+
}): unknown;
|
|
23
|
+
get(name: 'approval'): {
|
|
24
|
+
request(input: {
|
|
25
|
+
agent?: unknown;
|
|
26
|
+
toolName: string;
|
|
27
|
+
callId?: string;
|
|
28
|
+
reason: string;
|
|
29
|
+
signal?: AbortSignal;
|
|
30
|
+
}): Promise<string>;
|
|
31
|
+
} | undefined;
|
|
32
|
+
}
|
|
33
|
+
export declare function installSendApproval(ctx: ApprovalContext, runtime: Pick<EmailRuntime, 'getSettingsValue' | 'getEffectiveSettings'>): void;
|
|
34
|
+
export {};
|
package/lib/approval.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export function installSendApproval(ctx, runtime) {
|
|
2
|
+
ctx.on('tools/pre-execute', async (exec, next) => {
|
|
3
|
+
if (exec?.name !== 'email_send' && exec?.name !== 'email_reply')
|
|
4
|
+
return next();
|
|
5
|
+
const value = runtime.getSettingsValue();
|
|
6
|
+
if (value.sendApproval === false)
|
|
7
|
+
return next();
|
|
8
|
+
try {
|
|
9
|
+
runtime.getEffectiveSettings();
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return next(); // unconfigured: let the tool report the actionable hint
|
|
13
|
+
}
|
|
14
|
+
let reason;
|
|
15
|
+
if (exec.name === 'email_send') {
|
|
16
|
+
const args = (exec.arguments ?? {});
|
|
17
|
+
const attachCount = Array.isArray(args.attachments) ? args.attachments.length : 0;
|
|
18
|
+
reason = '发送邮件给 ' + args.to + ',主题「' + args.subject + '」' + (attachCount > 0 ? ',附件 ' + attachCount + ' 个' : '');
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
const args = (exec.arguments ?? {});
|
|
22
|
+
const mode = typeof args.mode === 'string' && args.mode.trim() !== '' ? args.mode.trim().toLowerCase() : 'reply';
|
|
23
|
+
const modeLabel = mode === 'forward' ? '转发' : mode === 'reply-all' ? '回复全部' : '回复';
|
|
24
|
+
reason = modeLabel + '邮件(原邮件 uid=' + args.uid + ')' + (mode === 'forward' && typeof args.to === 'string' && args.to.trim() !== '' ? ',收件人 ' + args.to : '');
|
|
25
|
+
}
|
|
26
|
+
// Gate-owned approval: we run the approval round-trip ourselves so the
|
|
27
|
+
// denial reason is always honest and actionable — including the Full
|
|
28
|
+
// Access case where the harness policy answers 'rejected' without ever
|
|
29
|
+
// showing a dialog.
|
|
30
|
+
const approval = ctx.get('approval');
|
|
31
|
+
if (approval === undefined) {
|
|
32
|
+
return {
|
|
33
|
+
kind: 'deny',
|
|
34
|
+
reason: 'email_send 需要确认,但当前环境没有审批通道(如 headless)。如确定安全,可在配置中设置 sendApproval: false 后直接发送。',
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
const outcome = await approval.request({
|
|
38
|
+
agent: exec.agent,
|
|
39
|
+
toolName: exec.name,
|
|
40
|
+
callId: exec.callId,
|
|
41
|
+
reason,
|
|
42
|
+
signal: exec.signal,
|
|
43
|
+
});
|
|
44
|
+
if (outcome === 'allowed-once')
|
|
45
|
+
return next();
|
|
46
|
+
if (outcome === 'cancelled')
|
|
47
|
+
return { kind: 'deny', reason: '发信确认被取消,邮件未发送。' };
|
|
48
|
+
if (outcome === 'unavailable')
|
|
49
|
+
return { kind: 'deny', reason: '发信确认不可用(没有可用的审批界面),邮件未发送。' };
|
|
50
|
+
return {
|
|
51
|
+
kind: 'deny',
|
|
52
|
+
reason: '发信未获批准:要么你拒绝了,要么当前会话处于 Full Access(审批策略 never,不会弹框)。若在 Full Access:切到 Read Only / Write 再发,或关闭 sendApproval(自行承担风险)。',
|
|
53
|
+
};
|
|
54
|
+
}, { prepend: true });
|
|
55
|
+
}
|
package/lib/index.d.ts
CHANGED
|
@@ -1,16 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { EmailConfig } from './config.js';
|
|
2
2
|
export declare const name = "tool-email";
|
|
3
3
|
export declare const inject: string[];
|
|
4
4
|
export type Config = EmailConfig;
|
|
5
|
-
/**
|
|
6
|
-
* 解析天级日期参数为 Date。接受 YYYY-MM-DD 或完整 ISO 时间。
|
|
7
|
-
* endInclusive=true 时返回“该日结束”(次日零点),用于 until 语义(IMAP BEFORE 是不含当天的)。
|
|
8
|
-
*/
|
|
9
|
-
export declare function parseEmailDay(input: string, label: string, endInclusive?: boolean): Date;
|
|
5
|
+
/** Compose settings/pool lifecycle, tools, browser routes and the outgoing-mail gate. */
|
|
10
6
|
export declare function apply(ctx: any, config?: Config): void;
|
|
11
|
-
export {
|
|
12
|
-
export {
|
|
13
|
-
export {
|
|
14
|
-
export {
|
|
15
|
-
export {
|
|
16
|
-
export {
|
|
7
|
+
export { clampInt, defaultDownloadDir, EMAIL_PASSWORD_ENV, parseAccountsYaml, PROVIDER_NAMES, resolveEmailConfig, resolveEmailSettings } from './config.js';
|
|
8
|
+
export { buildReplyMessage, EmailPool, extractMessageIds, MailError, messageMatchesQuery, messageOf, selectAttachmentPart, validateAttachmentPaths } from './mail-client.js';
|
|
9
|
+
export { flattenAddresses, parseRawMessage, sanitizeFilename, stripHtml, truncateText } from './parse.js';
|
|
10
|
+
export { EmailSettingsSchema, SETTINGS_NAMESPACE, toEmailConfig, toSettingsBase, validateSettingsValue } from './settings.js';
|
|
11
|
+
export { parseEmailDay } from './tool-contract.js';
|
|
12
|
+
export { EmailSettingsBackend, installEmailSettingsWeb, SETTINGS_ROUTE } from './web.js';
|