@eduwork/dsh-mail 0.1.0-alpha.5 → 0.1.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/CHANGELOG.en.md +9 -0
- package/CHANGELOG.md +9 -0
- package/CODE_OF_CONDUCT.en.md +17 -0
- package/CODE_OF_CONDUCT.md +17 -0
- package/CONTRIBUTING.en.md +9 -0
- package/CONTRIBUTING.md +23 -0
- package/GOVERNANCE.en.md +30 -0
- package/GOVERNANCE.md +30 -0
- package/MAINTAINERS.en.md +13 -0
- package/MAINTAINERS.md +13 -0
- package/README.en.md +10 -6
- package/README.md +9 -9
- package/SECURITY.en.md +2 -2
- package/SECURITY.md +2 -2
- package/SUPPORT.en.md +15 -0
- package/SUPPORT.md +15 -0
- package/THIRD_PARTY_NOTICES.en.md +1 -1
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/docs/EDUWORK-MIGRATION.en.md +3 -3
- package/docs/EDUWORK-MIGRATION.md +3 -3
- package/docs/architecture.en.md +3 -3
- package/docs/architecture.md +4 -4
- package/docs/compatibility.en.md +4 -4
- package/docs/compatibility.md +4 -4
- package/docs/decisions/0001-mail-assistant-boundary.en.md +1 -1
- package/docs/decisions/0001-mail-assistant-boundary.md +1 -1
- package/docs/getting-started.en.md +3 -3
- package/docs/getting-started.md +4 -4
- package/docs/release-checklist.en.md +5 -4
- package/docs/release-checklist.md +5 -4
- package/lib/client.js +40 -23
- package/lib/index.js +776 -978
- package/package.json +25 -11
package/docs/getting-started.md
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
## 前置条件
|
|
6
6
|
|
|
7
7
|
- Node.js 22+;
|
|
8
|
-
- DeepSeek Harness `0.1.2-
|
|
9
|
-
- Profile 中已经有 Settings、Credentials、Tools、Filesystem、Approval 和 Web Client;
|
|
8
|
+
- DeepSeek Harness `0.1.2-rc.1`;
|
|
9
|
+
- Profile 中已经有 Settings、Credentials、Tools、Filesystem、Permission Presets、Approval 和 Web Client;
|
|
10
10
|
- 一个开启了 IMAP/SMTP 的邮箱账号,以及邮箱服务商签发的客户端授权码或应用专用密码。
|
|
11
11
|
|
|
12
12
|
## 安装
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
普通使用固定经过复核的 npm 精确版本:
|
|
15
15
|
|
|
16
16
|
```bash
|
|
17
|
-
dsh plugin --profile web add @eduwork/dsh-mail@0.1.0
|
|
17
|
+
dsh plugin --profile web add @eduwork/dsh-mail@0.1.0
|
|
18
18
|
```
|
|
19
19
|
|
|
20
20
|
需要审计、开发或验证尚未发布的改动时,使用源码 checkout:
|
|
@@ -29,7 +29,7 @@ dsh plugin --profile web add .
|
|
|
29
29
|
|
|
30
30
|
安装只向 Profile 加入一个 Bundle。插件配置为空也能正常启动;不会在启动时探测服务器或安装额外运行时。
|
|
31
31
|
|
|
32
|
-
源码安装会把 checkout 链接进 Profile
|
|
32
|
+
源码安装会把 checkout 链接进 Profile,因此目录必须持续存在。团队部署应固定经复核的 npm 精确版本。
|
|
33
33
|
|
|
34
34
|
## 配置
|
|
35
35
|
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
[简体中文](release-checklist.md) | **English**
|
|
4
4
|
|
|
5
|
-
A release pull request
|
|
5
|
+
A release pull request or release record captures the maintainer decision, date, command-output links, independent technical-review evidence, and exceptions. A sole maintainer owns publication during the project's initial stage; do not invent a second human approver who does not exist. Any applicable incomplete security or publication check blocks a stable release. Additional maintainers and multi-person review remain future governance goals.
|
|
6
6
|
|
|
7
7
|
## Authority and metadata
|
|
8
8
|
|
|
9
|
-
- [ ]
|
|
9
|
+
- [ ] The current maintainer approved public release and the MIT license and recorded technical-review evidence and remaining risk.
|
|
10
10
|
- [ ] GitHub repository, npm name, homepage, issues, and private security-report URLs are correct.
|
|
11
11
|
- [ ] Version, changelog, compatibility, and install commands agree.
|
|
12
12
|
- [ ] Chinese is the default language and every Markdown document has an English mirror with reciprocal entries.
|
|
@@ -43,9 +43,10 @@ A release pull request records reviewers, date, command-output links, and except
|
|
|
43
43
|
|
|
44
44
|
## Supply chain and publication
|
|
45
45
|
|
|
46
|
-
- [ ] Clean `npm ci` and `npm run check` pass on Windows and Linux with Node 22.
|
|
46
|
+
- [ ] Clean `npm ci` and `npm run check` pass on Windows and Linux with Node 22/24.
|
|
47
47
|
- [ ] `npm audit`, CodeQL, direct/transitive licenses, and install scripts receive human review.
|
|
48
48
|
- [ ] GitHub Actions use immutable commits; Dependabot and branch protection are active.
|
|
49
49
|
- [ ] `npm pack --dry-run` and the extracted tarball contain only expected files; the Host bundle has no bare IMAP/SMTP imports.
|
|
50
50
|
- [ ] Tag, release notes, tarball checksum, deprecation, and rollback are ready.
|
|
51
|
-
- [ ]
|
|
51
|
+
- [ ] Publish the first stable release from the reviewed frozen package with the npm CLI and maintainer-confirmed browser 2FA; record that Trusted Publishing is not configured and make no provenance claim for this release.
|
|
52
|
+
- [ ] For later releases, enable OIDC/provenance only after verifying that npm Trusted Publishing is bound to this repository's `.github/workflows/release.yml` and environment `npm`; never use a long-lived publish token.
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
**简体中文** | [English](release-checklist.en.md)
|
|
4
4
|
|
|
5
|
-
发布 Pull Request
|
|
5
|
+
发布 Pull Request 或发布记录应记维护者决策、日期、命令输出链接、独立技术复核证据和例外。项目初期由单一维护者负责发布;不得虚构尚不存在的第二位人工批准人。任何适用且未完成的安全或发布检查都会阻止稳定发布;未来增加替补和多人审查作为治理目标记录。
|
|
6
6
|
|
|
7
7
|
## 授权与元数据
|
|
8
8
|
|
|
9
|
-
- [ ]
|
|
9
|
+
- [ ] 当前维护者确认公开发布和 MIT 许可证,并记录技术复核证据与剩余风险。
|
|
10
10
|
- [ ] GitHub 仓库、npm 包名、主页、Issue 和安全报告地址准确。
|
|
11
11
|
- [ ] 版本、Changelog、兼容性和安装命令一致。
|
|
12
12
|
- [ ] 默认文档为中文,每份 Markdown 文档都有英文镜像和双向入口。
|
|
@@ -43,9 +43,10 @@
|
|
|
43
43
|
|
|
44
44
|
## 供应链与发布
|
|
45
45
|
|
|
46
|
-
- [ ] Windows 与 Linux 使用 Node 22 完成干净 `npm ci` 和 `npm run check`。
|
|
46
|
+
- [ ] Windows 与 Linux 使用 Node 22/24 完成干净 `npm ci` 和 `npm run check`。
|
|
47
47
|
- [ ] `npm audit`、CodeQL、直接/间接许可证和安装脚本已人工审查。
|
|
48
48
|
- [ ] GitHub Actions 固定到不可变 commit,Dependabot 与分支保护有效。
|
|
49
49
|
- [ ] `npm pack --dry-run` 和解压后的 tarball 只包含预期文件;Host bundle 没有外部 IMAP/SMTP 裸导入。
|
|
50
50
|
- [ ] tag、发布说明、tarball checksum、弃用和回滚方案已准备。
|
|
51
|
-
- [ ]
|
|
51
|
+
- [ ] 首次稳定版从经复核的冻结包使用 npm CLI 发布,由维护者在浏览器完成 2FA;本次记录 Trusted Publishing 尚未配置,不声明 provenance。
|
|
52
|
+
- [ ] 后续发布在验证 npm Trusted Publisher 已绑定本仓库的 `.github/workflows/release.yml` 与 Environment `npm` 后,再启用 OIDC/provenance 发布路径;不使用长期 Token。
|
package/lib/client.js
CHANGED
|
@@ -33,8 +33,8 @@ window.__ModuleLoader__.load({
|
|
|
33
33
|
const inject = [
|
|
34
34
|
"slots",
|
|
35
35
|
"remote",
|
|
36
|
-
"remote.
|
|
37
|
-
"
|
|
36
|
+
"remote.credentials",
|
|
37
|
+
"settingsScope"
|
|
38
38
|
];
|
|
39
39
|
const NS = "dsh-mail-assistant";
|
|
40
40
|
const PASSWORD_REF = "DSH_MAIL_ASSISTANT_PASSWORD";
|
|
@@ -90,6 +90,8 @@ window.__ModuleLoader__.load({
|
|
|
90
90
|
usage: "保存后,在对话里让 Agent“查一下最近邮件”即可验证。插件不会在启动或保存时主动连接邮箱。",
|
|
91
91
|
error: "保存失败",
|
|
92
92
|
reload: "重新加载",
|
|
93
|
+
unavailable: "当前 DSH 连接未提供此设置命名空间。",
|
|
94
|
+
readOnly: "当前连接为只读模式,无法保存设置。",
|
|
93
95
|
credentialPartial: "授权码已经保存,但其他配置保存失败;请修正后再次保存。"
|
|
94
96
|
} : {
|
|
95
97
|
nav: "Mail assistant",
|
|
@@ -143,6 +145,8 @@ window.__ModuleLoader__.load({
|
|
|
143
145
|
usage: "After saving, ask the agent to find recent mail. The plugin never connects to a mailbox during startup or save.",
|
|
144
146
|
error: "Save failed",
|
|
145
147
|
reload: "Reload",
|
|
148
|
+
unavailable: "This DSH connection does not expose the mail settings namespace.",
|
|
149
|
+
readOnly: "This connection exposes settings in read-only mode.",
|
|
146
150
|
credentialPartial: "The app password was stored, but the remaining settings failed to save. Fix the form and save again."
|
|
147
151
|
};
|
|
148
152
|
const defaults = {
|
|
@@ -219,33 +223,37 @@ window.__ModuleLoader__.load({
|
|
|
219
223
|
}
|
|
220
224
|
function MailSettings({ service }) {
|
|
221
225
|
const [draft, setDraft] = (0, react.useState)(null);
|
|
222
|
-
const [revision, setRevision] = (0, react.useState)(0);
|
|
223
226
|
const [passwordConfigured, setPasswordConfigured] = (0, react.useState)(false);
|
|
227
|
+
const [credentialWritable, setCredentialWritable] = (0, react.useState)(false);
|
|
224
228
|
const [password, setPassword] = (0, react.useState)("");
|
|
225
229
|
const [busy, setBusy] = (0, react.useState)(false);
|
|
226
230
|
const [notice, setNotice] = (0, react.useState)("");
|
|
227
231
|
const [error, setError] = (0, react.useState)("");
|
|
228
232
|
const [preset, setPreset] = (0, react.useState)("custom");
|
|
229
233
|
const [advancedOpen, setAdvancedOpen] = (0, react.useState)(false);
|
|
234
|
+
const subscribe = (0, react.useMemo)(() => service.scope.subscribe.bind(service.scope), [service.scope]);
|
|
235
|
+
const getSnapshot = (0, react.useMemo)(() => service.scope.getSnapshot.bind(service.scope), [service.scope]);
|
|
236
|
+
const snapshot = (0, react.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
|
|
230
237
|
const load = async () => {
|
|
231
238
|
setError("");
|
|
232
239
|
setNotice("");
|
|
233
|
-
const
|
|
234
|
-
const namespace = settings.namespaces.find((candidate) => candidate.ns === NS);
|
|
235
|
-
if (!namespace) throw new Error("dsh-mail-assistant settings namespace is not available");
|
|
236
|
-
const next = {
|
|
237
|
-
...defaults,
|
|
238
|
-
...namespace.value ?? {}
|
|
239
|
-
};
|
|
240
|
-
setDraft(next);
|
|
241
|
-
setPreset(detectPreset(next));
|
|
242
|
-
setRevision(namespace.revision ?? 0);
|
|
240
|
+
const credentials = await unwrap(service.credentials.describe([PASSWORD_REF]));
|
|
243
241
|
setPasswordConfigured(credentials[PASSWORD_REF]?.configured === true);
|
|
242
|
+
setCredentialWritable(credentials[PASSWORD_REF]?.writable === true);
|
|
244
243
|
setPassword("");
|
|
245
244
|
};
|
|
246
245
|
(0, react.useEffect)(() => {
|
|
247
246
|
load().catch((cause) => setError(cause?.message || String(cause)));
|
|
248
247
|
}, []);
|
|
248
|
+
(0, react.useEffect)(() => {
|
|
249
|
+
if (snapshot.status !== "ready") return;
|
|
250
|
+
const next = {
|
|
251
|
+
...defaults,
|
|
252
|
+
...snapshot.value ?? {}
|
|
253
|
+
};
|
|
254
|
+
setDraft(next);
|
|
255
|
+
setPreset(detectPreset(next));
|
|
256
|
+
}, [snapshot]);
|
|
249
257
|
const set = (key, value) => setDraft((current) => ({
|
|
250
258
|
...current,
|
|
251
259
|
[key]: value
|
|
@@ -269,12 +277,12 @@ window.__ModuleLoader__.load({
|
|
|
269
277
|
setPasswordConfigured(true);
|
|
270
278
|
setPassword("");
|
|
271
279
|
}
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
});
|
|
277
|
-
|
|
280
|
+
const ops = Object.entries(draft).map(([field, value]) => ({
|
|
281
|
+
op: "set",
|
|
282
|
+
path: [field],
|
|
283
|
+
value
|
|
284
|
+
}));
|
|
285
|
+
await service.scope.mutate(ops, snapshot.revision);
|
|
278
286
|
setNotice(copy.saved);
|
|
279
287
|
} catch (cause) {
|
|
280
288
|
setError(passwordStored ? copy.credentialPartial : cause?.message || `${copy.error}`);
|
|
@@ -361,7 +369,7 @@ window.__ModuleLoader__.load({
|
|
|
361
369
|
};
|
|
362
370
|
if (!draft) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
363
371
|
style: styles.root,
|
|
364
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: error || copy.loading }), error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
372
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: error || (snapshot.status === "unavailable" ? copy.unavailable : copy.loading) }), error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
365
373
|
style: styles.button,
|
|
366
374
|
onClick: () => load(),
|
|
367
375
|
children: copy.reload
|
|
@@ -531,6 +539,7 @@ window.__ModuleLoader__.load({
|
|
|
531
539
|
type: "password",
|
|
532
540
|
autoComplete: "new-password",
|
|
533
541
|
value: password,
|
|
542
|
+
disabled: busy || !credentialWritable,
|
|
534
543
|
placeholder: copy.credentialPlaceholder,
|
|
535
544
|
onChange: (event) => setPassword(event.currentTarget.value),
|
|
536
545
|
style: styles.input
|
|
@@ -538,7 +547,7 @@ window.__ModuleLoader__.load({
|
|
|
538
547
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: copy.passwordHint }),
|
|
539
548
|
passwordConfigured && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
540
549
|
type: "button",
|
|
541
|
-
disabled: busy,
|
|
550
|
+
disabled: busy || !credentialWritable,
|
|
542
551
|
style: {
|
|
543
552
|
...styles.button,
|
|
544
553
|
justifySelf: "start",
|
|
@@ -710,6 +719,14 @@ window.__ModuleLoader__.load({
|
|
|
710
719
|
},
|
|
711
720
|
children: copy.usage
|
|
712
721
|
}),
|
|
722
|
+
!snapshot.writable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
723
|
+
role: "status",
|
|
724
|
+
style: {
|
|
725
|
+
color: colors.secondary,
|
|
726
|
+
fontSize: 12
|
|
727
|
+
},
|
|
728
|
+
children: copy.readOnly
|
|
729
|
+
}),
|
|
713
730
|
error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
714
731
|
role: "alert",
|
|
715
732
|
style: {
|
|
@@ -728,7 +745,7 @@ window.__ModuleLoader__.load({
|
|
|
728
745
|
}),
|
|
729
746
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
730
747
|
type: "button",
|
|
731
|
-
disabled: busy,
|
|
748
|
+
disabled: busy || !snapshot.writable,
|
|
732
749
|
onClick: save,
|
|
733
750
|
style: styles.primary,
|
|
734
751
|
children: busy ? copy.saving : copy.save
|
|
@@ -738,7 +755,7 @@ window.__ModuleLoader__.load({
|
|
|
738
755
|
}
|
|
739
756
|
function apply(ctx) {
|
|
740
757
|
const service = {
|
|
741
|
-
|
|
758
|
+
scope: ctx.settingsScope.bind({ namespace: NS }),
|
|
742
759
|
credentials: ctx.remote.credentials
|
|
743
760
|
};
|
|
744
761
|
ctx.slots.inject("settings.section", () => ctx.slots.register({
|