@dsh-plus/notify-email 0.1.21 → 0.1.23
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/lib/client.js +4 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.js +12 -1
- package/package.json +2 -2
- package/src/client/card.tsx +2 -1
- package/src/client/client.ts +4 -24
- package/src/client/i18n.ts +10 -4
- package/src/config-api.ts +2 -0
- package/src/mailer.ts +4 -2
- package/src/watchers/decision.ts +3 -0
- package/src/watchers/turn-end.ts +20 -3
package/lib/client.js
CHANGED
|
@@ -168,7 +168,10 @@ window.__ModuleLoader__.load({
|
|
|
168
168
|
confirm: "Confirm",
|
|
169
169
|
cancel: "Cancel"
|
|
170
170
|
};
|
|
171
|
-
/**
|
|
171
|
+
/**
|
|
172
|
+
* own 覆盖同键合并(返回新对象,入参不可变)。
|
|
173
|
+
* 返回类型保留两侧键集合,调用方据此派生 DictKey。
|
|
174
|
+
*/
|
|
172
175
|
function mergeDict(base, own) {
|
|
173
176
|
return {
|
|
174
177
|
...base,
|
package/lib/index.d.ts
CHANGED
|
@@ -148,7 +148,7 @@ declare class NotifyEmailService extends Service {
|
|
|
148
148
|
//#endregion
|
|
149
149
|
//#region src/index.d.ts
|
|
150
150
|
declare const name = "dsh-plus-notify-email";
|
|
151
|
-
declare const inject: readonly [
|
|
151
|
+
declare const inject: readonly ['agents', 'tools', 'credentials'];
|
|
152
152
|
declare function apply(ctx: Context, config: NotifyEmailConfig): void;
|
|
153
153
|
//#endregion
|
|
154
154
|
export { Config, type DecisionCall, type EmailNotice, NotifyEmailService, type NotifyTrigger, type TurnEndInfo, apply, inject, name };
|
package/lib/index.js
CHANGED
|
@@ -355,6 +355,17 @@ function extractErrorMessage(reason) {
|
|
|
355
355
|
const message = error.message;
|
|
356
356
|
return typeof message === "string" ? message : void 0;
|
|
357
357
|
}
|
|
358
|
+
/**
|
|
359
|
+
* agent 是否仍留有已接收但未认领的工作。
|
|
360
|
+
* `hasPending` 未进 Inbox 契约面——它只是 dsh-agent-loop 的 ReactLoopInbox
|
|
361
|
+
* 实现 getter(官方唯一的 `agent.inbox` 赋值点),故按「实现面存在才读」
|
|
362
|
+
* 收窄:实现缺席即视为无待处理工作;存在时读到的正是该 getter,取值与
|
|
363
|
+
* 契约面的 nextTurn/nextStep 非空判定一致。
|
|
364
|
+
*/
|
|
365
|
+
function hasPendingWork(inbox) {
|
|
366
|
+
if (!("hasPending" in inbox)) return false;
|
|
367
|
+
return inbox.hasPending === true;
|
|
368
|
+
}
|
|
358
369
|
function installTurnEndWatcher(ctx, service, resolveConfig) {
|
|
359
370
|
const watches = /* @__PURE__ */ new Map();
|
|
360
371
|
const logger = ctx.logger("notify-email");
|
|
@@ -369,7 +380,7 @@ function installTurnEndWatcher(ctx, service, resolveConfig) {
|
|
|
369
380
|
const fire = (session, turn, kind, errorMessage) => {
|
|
370
381
|
const agent = ctx.agents.get(session.id);
|
|
371
382
|
if (agent === void 0) return;
|
|
372
|
-
if (agent.status !== "idle" || agent.inbox
|
|
383
|
+
if (agent.status !== "idle" || hasPendingWork(agent.inbox)) return;
|
|
373
384
|
if (!ctx.agents.roots().includes(agent)) return;
|
|
374
385
|
const watch = watchOf(session.id);
|
|
375
386
|
const key = `${turn}:${kind}`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dsh-plus/notify-email",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.23",
|
|
4
4
|
"description": "dsh-plus service+ui plugin: 任务结束/等待决策/出错时向指定邮箱发送邮件通知",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"nodemailer": "9.0.6",
|
|
35
|
-
"@dsh-plus/shared": "0.1.
|
|
35
|
+
"@dsh-plus/shared": "0.1.16"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
38
|
"@deepseek-ai/cordis": "^4.0.2",
|
package/src/client/card.tsx
CHANGED
|
@@ -22,9 +22,10 @@ import {
|
|
|
22
22
|
import { type ReactElement, useEffect, useMemo, useState, useSyncExternalStore } from 'react'
|
|
23
23
|
import { SETTINGS_NS } from '../ns.ts'
|
|
24
24
|
import { sendTest } from './api.ts'
|
|
25
|
+
import type { Translate } from './i18n.ts'
|
|
25
26
|
|
|
26
27
|
export interface CardProps {
|
|
27
|
-
t
|
|
28
|
+
t: Translate
|
|
28
29
|
scope: Scope
|
|
29
30
|
api: NamespaceSettingsApi
|
|
30
31
|
}
|
package/src/client/client.ts
CHANGED
|
@@ -19,39 +19,19 @@ import {
|
|
|
19
19
|
createNamespaceApi,
|
|
20
20
|
createSettingsScope,
|
|
21
21
|
injectCardStyle,
|
|
22
|
-
type
|
|
22
|
+
type PluginClientContext,
|
|
23
23
|
} from '@dsh-plus/shared/client'
|
|
24
24
|
import { SETTINGS_NS } from '../ns.ts'
|
|
25
25
|
import { NotifyEmailCard } from './card.tsx'
|
|
26
|
-
import { en, NS, zh } from './i18n.ts'
|
|
26
|
+
import { type DictKey, en, NS, zh } from './i18n.ts'
|
|
27
27
|
|
|
28
28
|
export const name = 'dsh-plus-notify-email'
|
|
29
29
|
|
|
30
30
|
/** 浏览器半需要的 cordis 服务 key(loader 据此注入;package.json 的 dsh.client.inject 管包加载顺序)。 */
|
|
31
31
|
export const inject = ['slots', 'locale', 'remote', 'remote.settings'] as const
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
register(options: Record<string, unknown>, component: unknown): () => void
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
interface LocaleLike {
|
|
39
|
-
register(ns: string, dict: { zh: Record<string, string>; en: Record<string, string> }): () => void
|
|
40
|
-
bind(ns: string): (key: string) => string
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
interface RemoteLike {
|
|
44
|
-
settings: SettingsRemoteFace
|
|
45
|
-
$on(event: string, listener: (payload?: unknown) => void): () => void
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
interface ClientContext {
|
|
49
|
-
slots: SlotsLike
|
|
50
|
-
locale: LocaleLike
|
|
51
|
-
get(key: 'remote'): RemoteLike
|
|
52
|
-
on(event: string, listener: () => void): () => void
|
|
53
|
-
effect(execute: () => () => void, label?: string): unknown
|
|
54
|
-
}
|
|
33
|
+
/** 宿主窄面收编在 @dsh-plus/shared/client;TKey 传入本包 DictKey 使键名受检。 */
|
|
34
|
+
type ClientContext = PluginClientContext<DictKey>
|
|
55
35
|
|
|
56
36
|
export function apply(ctx: Context): void {
|
|
57
37
|
const c = ctx as unknown as ClientContext
|
package/src/client/i18n.ts
CHANGED
|
@@ -43,7 +43,7 @@ const ownZh = {
|
|
|
43
43
|
testDryRun: '已按 dry-run 记录日志(未真实发送)。',
|
|
44
44
|
testFailed: '测试发送失败:',
|
|
45
45
|
incomplete: 'SMTP 配置不完整(服务器/发件地址/收件人必填)。',
|
|
46
|
-
}
|
|
46
|
+
} as const
|
|
47
47
|
|
|
48
48
|
const ownEn = {
|
|
49
49
|
title: 'Email notifications',
|
|
@@ -81,7 +81,13 @@ const ownEn = {
|
|
|
81
81
|
testDryRun: 'Logged in dry-run mode (not sent).',
|
|
82
82
|
testFailed: 'Test send failed: ',
|
|
83
83
|
incomplete: 'SMTP config incomplete (host/from/recipients required).',
|
|
84
|
-
}
|
|
84
|
+
} as const
|
|
85
85
|
|
|
86
|
-
export
|
|
87
|
-
|
|
86
|
+
export type DictKey = keyof typeof commonZh | keyof typeof ownZh
|
|
87
|
+
|
|
88
|
+
/** 卡片/面板共用的翻译函数类型(slot 注入的 bind 结果按此消费)。 */
|
|
89
|
+
export type Translate = (key: DictKey) => string
|
|
90
|
+
|
|
91
|
+
// 标注为 Record<DictKey, string>:en 缺任一键即编译期报错(中英强制对齐)。
|
|
92
|
+
export const zh: Record<DictKey, string> = mergeDict(commonZh, ownZh)
|
|
93
|
+
export const en: Record<DictKey, string> = mergeDict(commonEn, ownEn)
|
package/src/config-api.ts
CHANGED
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
import type { IncomingMessage, ServerResponse } from 'node:http'
|
|
8
8
|
|
|
9
9
|
import type { Context } from '@deepseek-ai/cordis'
|
|
10
|
+
// 平台声明导入(无值消费):把 webServer 服务面合进 program,ctx.webServer 方可解析。
|
|
11
|
+
import type {} from '@deepseek-ai/dsh-host-webserver'
|
|
10
12
|
|
|
11
13
|
import type { NotifyEmailService } from './service.ts'
|
|
12
14
|
|
package/src/mailer.ts
CHANGED
|
@@ -83,8 +83,10 @@ export class Mailer {
|
|
|
83
83
|
private readonly resolveConfig: () => NotifyEmailConfig
|
|
84
84
|
private readonly logger: MailLogger
|
|
85
85
|
private readonly transport: Transport
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
// 构造期无条件赋值,故为「必需但可为 undefined」:exactOptionalPropertyTypes
|
|
87
|
+
// 下可选属性不允许显式赋 undefined。
|
|
88
|
+
private readonly audit: AuditSink | undefined
|
|
89
|
+
private readonly credentials: CredentialsSeamLike | undefined
|
|
88
90
|
|
|
89
91
|
constructor(
|
|
90
92
|
resolveConfig: () => NotifyEmailConfig,
|
package/src/watchers/decision.ts
CHANGED
|
@@ -5,6 +5,9 @@
|
|
|
5
5
|
* @module notify-email/watchers/decision
|
|
6
6
|
*/
|
|
7
7
|
import type { Context } from '@deepseek-ai/cordis'
|
|
8
|
+
// 平台声明导入(无值消费):把 dsh-tools 的 'tools/pre-execute' 事件与
|
|
9
|
+
// ToolExecution 参数/next 签名合进 program(事件名真名即此,非拼写错误)。
|
|
10
|
+
import type {} from '@deepseek-ai/dsh-tools'
|
|
8
11
|
|
|
9
12
|
import type { NotifyEmailService } from '../service.ts'
|
|
10
13
|
|
package/src/watchers/turn-end.ts
CHANGED
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
* @module notify-email/watchers/turn-end
|
|
8
8
|
*/
|
|
9
9
|
import type { Context } from '@deepseek-ai/cordis'
|
|
10
|
+
// 平台声明导入:把 agent 服务面(ctx.agents)与 Inbox 契约合进 program。
|
|
11
|
+
import type { Inbox } from '@deepseek-ai/dsh-agent'
|
|
10
12
|
import type { Session, TurnEndReason } from '@deepseek-ai/dsh-session'
|
|
11
13
|
|
|
12
14
|
import type { NotifyEmailConfig } from '../config.ts'
|
|
@@ -16,8 +18,11 @@ import type { NotifyEmailService } from '../service.ts'
|
|
|
16
18
|
const NOTIFY_KINDS = new Set(['completed', 'error', 'aborted'])
|
|
17
19
|
|
|
18
20
|
interface SessionWatch {
|
|
19
|
-
|
|
20
|
-
timer
|
|
21
|
+
// 显式并上 undefined:这些字段会被反复赋 undefined 以清空状态
|
|
22
|
+
// (如 `watch.timer = undefined` 取消定时器),exactOptionalPropertyTypes
|
|
23
|
+
// 下可选属性不允许该写法。
|
|
24
|
+
lastDelivery?: string | undefined
|
|
25
|
+
timer?: ReturnType<typeof setTimeout> | undefined
|
|
21
26
|
readonly notified: Set<string>
|
|
22
27
|
}
|
|
23
28
|
|
|
@@ -44,6 +49,18 @@ function extractErrorMessage(reason: unknown): string | undefined {
|
|
|
44
49
|
return typeof message === 'string' ? message : undefined
|
|
45
50
|
}
|
|
46
51
|
|
|
52
|
+
/**
|
|
53
|
+
* agent 是否仍留有已接收但未认领的工作。
|
|
54
|
+
* `hasPending` 未进 Inbox 契约面——它只是 dsh-agent-loop 的 ReactLoopInbox
|
|
55
|
+
* 实现 getter(官方唯一的 `agent.inbox` 赋值点),故按「实现面存在才读」
|
|
56
|
+
* 收窄:实现缺席即视为无待处理工作;存在时读到的正是该 getter,取值与
|
|
57
|
+
* 契约面的 nextTurn/nextStep 非空判定一致。
|
|
58
|
+
*/
|
|
59
|
+
function hasPendingWork(inbox: Inbox): boolean {
|
|
60
|
+
if (!('hasPending' in inbox)) return false
|
|
61
|
+
return inbox.hasPending === true
|
|
62
|
+
}
|
|
63
|
+
|
|
47
64
|
export function installTurnEndWatcher(
|
|
48
65
|
ctx: Context,
|
|
49
66
|
service: NotifyEmailService,
|
|
@@ -64,7 +81,7 @@ export function installTurnEndWatcher(
|
|
|
64
81
|
const fire = (session: Session, turn: number, kind: string, errorMessage?: string): void => {
|
|
65
82
|
const agent = ctx.agents.get(session.id)
|
|
66
83
|
if (agent === undefined) return
|
|
67
|
-
if (agent.status !== 'idle' || agent.inbox
|
|
84
|
+
if (agent.status !== 'idle' || hasPendingWork(agent.inbox)) return
|
|
68
85
|
if (!ctx.agents.roots().includes(agent)) return
|
|
69
86
|
const watch = watchOf(session.id)
|
|
70
87
|
const key = `${turn}:${kind}`
|