@goodandready/dsh-plugin-notify 0.3.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.md +91 -0
- package/LICENSE +21 -0
- package/README.md +210 -0
- package/README.ru.md +199 -0
- package/README.zh.md +198 -0
- package/cordis.patch.yml +3 -0
- package/lib/client.js +714 -0
- package/lib/index.js +444 -0
- package/package.json +60 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
5
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [0.3.0] - 2026-09-18
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- **Web Audio Chimes (`enableSound`)**:
|
|
11
|
+
- Synthesized dual-tone pentatonic chimes using Web Audio API (`AudioContext`) without external audio files or network requests (`lib/client.js`).
|
|
12
|
+
- Distinct pleasant sound signatures for `task_done`, `error`, and `approval_requested`.
|
|
13
|
+
- Added "Test sound" action button in the plugin settings card.
|
|
14
|
+
- **Cross-Session In-App Screen Toasts (`enableToasts`)**:
|
|
15
|
+
- Light-weight native toast banner overlay styled with DSH design variables (`--dsw-alias-*`, `data-dsh-plugin="dsh-plugin-notify"`).
|
|
16
|
+
- Shows event title, session ID, and summary across active and background sessions.
|
|
17
|
+
- Interactive "Go to session" navigation button to instantly switch to the target session.
|
|
18
|
+
- Auto-dismisses after 7 seconds or manual close.
|
|
19
|
+
- Added "Test toast" action button in settings.
|
|
20
|
+
- **Native OS / Desktop Notifications (`enableDesktopNotifications`)**:
|
|
21
|
+
- Integrates with HTML5 `Notification API` for system tray / desktop push notifications (Windows, macOS, Linux, and DSH Desktop app).
|
|
22
|
+
- Includes permission request button in the settings card with live state indicators (`granted`, `denied`, `default`).
|
|
23
|
+
- Clicking on the native notification focuses the DSH window and switches to the affected session.
|
|
24
|
+
- **Server-Sent Events (SSE) Event Stream (`GET /dsh-plugin-notify/events`)**:
|
|
25
|
+
- Live broadcast endpoint mounted on Cordis `webServer` service (`lib/index.js`).
|
|
26
|
+
- Real-time client listener in `lib/client.js` with exponential backoff auto-reconnect.
|
|
27
|
+
- Configurable `notifyBackgroundOnly` filter to suppress notifications if the event occurred in the currently active session.
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
- **Settings Card Duplication (#16)**:
|
|
31
|
+
- Removed fallback registration to `settings.section` slot in `lib/client.js`, retaining only `settings.plugin.item`.
|
|
32
|
+
- Prevents the notify settings card from rendering twice (in root Settings and inside Plugins).
|
|
33
|
+
|
|
34
|
+
### Changed
|
|
35
|
+
- **Public Package Migration**:
|
|
36
|
+
- Migrated package scope from `@goodandready-private/dsh-plugin-notify` to public `@goodandready/dsh-plugin-notify` on npm and GitHub.
|
|
37
|
+
- **Repository Hygiene and Clean Tree (#10)**:
|
|
38
|
+
- Untracked internal agent instructions (`AGENTS.md`, `index.md`, `deploy.sh`, `docs/testing/`, `docs/deployment/`) from public git tracking while preserving them on local disk.
|
|
39
|
+
- Added sanitized GitHub publishing layer (`publish.sh`) and `.gitattributes` export-ignores.
|
|
40
|
+
- **CI Test Suite and Leak Verification (#13)**:
|
|
41
|
+
- Added comprehensive GitHub Actions workflow (`.github/workflows/ci.yml`) and updated Gitea CI (`.gitea/workflows/ci.yml`).
|
|
42
|
+
- Added unit test suite for delivery channels, error resilience, timeouts, sound/toast options, and locale deduplication (15/15 pass).
|
|
43
|
+
- **Opt-in Notification Channels (#20)**:
|
|
44
|
+
- `enableSound`, `enableToasts`, and `enableDesktopNotifications` now default to `false` (opt-in) to prevent unexpected noise or unprompted browser permission popups.
|
|
45
|
+
- Browser notification permission is requested exclusively via explicit user interaction ("Allow desktop notifications" button in the settings card).
|
|
46
|
+
- Web Audio `AudioContext` is managed lazily with graceful failure handling if blocked by browser autoplay policies.
|
|
47
|
+
- **Fail-Closed SSE Origin Guard (#21)**:
|
|
48
|
+
- Added strict request validation (`isTrustedRequest`) on `GET /dsh-plugin-notify/events` checking `sec-fetch-site` (`same-origin`/`same-site`), origin/host match, loopback remote address, or DSH authorization headers.
|
|
49
|
+
- Returns `403 Forbidden` for untrusted cross-origin requests.
|
|
50
|
+
- Removed wildcard `Access-Control-Allow-Origin: *` header.
|
|
51
|
+
- **Diagnostics via ctx.logger (#22)**:
|
|
52
|
+
- Replaced all server-side `console.*` calls with `ctx.logger` (`debug` for delivery events, `warn` for resolve/post failures).
|
|
53
|
+
- Diagnostic logs are now properly captured by DSH logging infrastructure instead of leaking to raw process stdout.
|
|
54
|
+
- **Schemastery Dependency Architecture (#23)**:
|
|
55
|
+
- Moved `@deepseek-ai/schemastery` from `dependencies` to `peerDependencies` (with `devDependencies` for test runner), ensuring consistency with DSH ecosystem peer dependency contracts.
|
|
56
|
+
- **Documentation Hygiene (#17)**:
|
|
57
|
+
- Removed `Changed in vX.Y.Z` sections from `README.md`, `README.ru.md`, and `README.zh.md`, centralizing all version release history into `CHANGELOG.md`.
|
|
58
|
+
- Registered `CHANGELOG.md` in `package.json` distribution files list.
|
|
59
|
+
|
|
60
|
+
## [0.2.5] - 2026-09-16
|
|
61
|
+
|
|
62
|
+
### Changed
|
|
63
|
+
- Runtime sources live in `lib/` instead of a misleading `dist/` tree; removed unnecessary TypeScript build config.
|
|
64
|
+
- The settings card stylesheet is tagged `data-dsh-plugin="dsh-plugin-notify"` so HMR and neighbor-plugin cleanup preserve card styles.
|
|
65
|
+
- Product README provided in English, Chinese, and Russian; internal files (`AGENTS.md`, `index.md`) stay in Gitea and are excluded from npm packaging.
|
|
66
|
+
|
|
67
|
+
### Added
|
|
68
|
+
- Automated unit test suite (`test/notify.test.mjs`) covering IM channel payloads, missing credentials, recipient failures, AbortSignal timeout, and locale reload without `ru` dictionary.
|
|
69
|
+
|
|
70
|
+
## [0.2.4] - 2026-09-08
|
|
71
|
+
|
|
72
|
+
### Fixed
|
|
73
|
+
- Avoid duplicate Russian locale registration to cleanly coexist with the `dsh-russian-lang` language pack.
|
|
74
|
+
|
|
75
|
+
### Changed
|
|
76
|
+
- Pointed repository and issue tracker URLs to `goodandready-private` organization.
|
|
77
|
+
|
|
78
|
+
## [0.2.3] - 2026-09-06
|
|
79
|
+
|
|
80
|
+
### Added
|
|
81
|
+
- Native DSH settings card registered on `settings.plugin.item`.
|
|
82
|
+
- Credential references for webhook URLs: secrets stored in DSH Credentials service, settings card stores only credential names.
|
|
83
|
+
|
|
84
|
+
### Changed
|
|
85
|
+
- Standardized MIT license with GooDAnDReaDY copyright.
|
|
86
|
+
- Migrated to private package route `@goodandready-private/dsh-plugin-notify`.
|
|
87
|
+
|
|
88
|
+
## [0.2.2] - 2026-09-05
|
|
89
|
+
|
|
90
|
+
### Added
|
|
91
|
+
- Initial private package routing and credentials-based webhook dispatch for Feishu, WeCom, DingTalk, Slack, Discord, and generic custom webhooks.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 GooDAnDReaDY
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# 📦 @goodandready/dsh-plugin-notify
|
|
2
|
+
|
|
3
|
+
<div align="center">
|
|
4
|
+
|
|
5
|
+
<h3>Remote IM webhooks for turn done, errors, and approval waits</h3>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-10b981.svg?style=for-the-badge&labelColor=064e3b" alt="license"></a>
|
|
9
|
+
<a href="https://github.com/topics/dsh-plugin"><img src="https://img.shields.io/badge/DSH-Plugin-8b5cf6.svg?style=for-the-badge&labelColor=2e1065" alt="DSH Plugin"></a>
|
|
10
|
+
<a href="https://nodejs.org"><img src="https://img.shields.io/badge/Node-20%2B-f59e0b.svg?style=for-the-badge&labelColor=451a03" alt="Node version"></a>
|
|
11
|
+
</p>
|
|
12
|
+
|
|
13
|
+
<p align="center">
|
|
14
|
+
<a href="https://goodandready.app/"><img src="https://img.shields.io/badge/🌐_DSH_Hub-goodandready.app-ff4500.svg?style=for-the-badge&labelColor=1a1a2e" alt="GoodAndReady Showcase"></a>
|
|
15
|
+
</p>
|
|
16
|
+
|
|
17
|
+
<p align="center">
|
|
18
|
+
<a href="README.md"><b>🇬🇧 English</b></a> •
|
|
19
|
+
<a href="README.zh.md"><b>🇨🇳 中文说明</b></a> •
|
|
20
|
+
<a href="README.ru.md"><b>🇷🇺 Русский</b></a>
|
|
21
|
+
</p>
|
|
22
|
+
|
|
23
|
+
<table align="center">
|
|
24
|
+
<tr>
|
|
25
|
+
<td align="center">
|
|
26
|
+
⭐ <strong>If you like this plugin, please star it on GitHub</strong> — it shows me that the plugin is useful to you and motivates me to keep developing it.
|
|
27
|
+
<br><br>
|
|
28
|
+
🐛 <strong>If you find a bug or would like to request a feature</strong>, open a GitHub issue in any language — I will review your proposal and implement useful suggestions in a future plugin version.
|
|
29
|
+
</td>
|
|
30
|
+
</tr>
|
|
31
|
+
</table>
|
|
32
|
+
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Overview / The Problem
|
|
38
|
+
|
|
39
|
+
DeepSeek Harness already knows when a turn finished, failed, or is waiting for approval. Without this plugin, those events remain confined inside the session. If you are working across multiple parallel sessions or minimized in another app, you have to keep manually checking back.
|
|
40
|
+
|
|
41
|
+
This plugin bridges that gap across four flexible notification layers:
|
|
42
|
+
1. **Audio Chimes**: Synthesized Web Audio pentatonic chimes that play immediately upon turn completion, failure, or approval requests without external audio files.
|
|
43
|
+
2. **Cross-Session Toasts**: On-screen floating banners that alert you across sessions with an interactive **"Go to session"** button to jump directly to the session that fired the event.
|
|
44
|
+
3. **Desktop / OS Push Notifications**: Native Windows, macOS, Linux, and DSH Desktop notifications via HTML5 `Notification API` with window focus and session switching on click.
|
|
45
|
+
4. **Remote IM Webhooks**: Outbound JSON webhooks for Feishu, WeCom, DingTalk, Slack, Discord, or generic custom endpoints. Webhook URLs are kept secret in DSH Credentials.
|
|
46
|
+
|
|
47
|
+
## Architecture
|
|
48
|
+
|
|
49
|
+
```mermaid
|
|
50
|
+
graph TD
|
|
51
|
+
A[DSH session/event] --> B[plugin-notify host]
|
|
52
|
+
B -->|credential name| C[Credentials service]
|
|
53
|
+
C -->|webhook URL| B
|
|
54
|
+
B -->|POST JSON| D[Feishu / WeCom / DingTalk / Slack / Discord / custom]
|
|
55
|
+
B -.->|macOS only| E[osascript notification]
|
|
56
|
+
B -->|SSE stream: /dsh-plugin-notify/events| F[Client Listener lib/client.js]
|
|
57
|
+
F -->|Web Audio API| G[Audio Chimes]
|
|
58
|
+
F -->|DOM overlay| H[Cross-Session Toasts]
|
|
59
|
+
F -->|Notification API| I[Desktop / OS Push]
|
|
60
|
+
J[Settings Card] -->|configuration| B
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Feature breakdown
|
|
64
|
+
|
|
65
|
+
### Host (`lib/index.js`)
|
|
66
|
+
|
|
67
|
+
- Subscribes to `session/event`.
|
|
68
|
+
- `turn/end` with `reason.kind === 'completed'` → `task_done`.
|
|
69
|
+
- `turn/end` with any other reason → `error`.
|
|
70
|
+
- `approval/asked` → `approval_requested`.
|
|
71
|
+
- Streams real-time notifications to connected clients via SSE (`GET /dsh-plugin-notify/events`) on the Cordis `webServer` service.
|
|
72
|
+
- Resolves each `webhooks.*` value as: legacy `http(s)://` URL (deprecated warning) → Credentials `resolve(credentialRef(name))` → `process.env[name]`.
|
|
73
|
+
- Posts with `AbortSignal.timeout(timeoutMs)` (default 5000 ms). Failed POSTs are logged and never retried, and they never block the agent loop.
|
|
74
|
+
- Optional DND window (`HH:MM`, including overnight ranges). Events are still observed; webhooks, chimes, and local popups are skipped.
|
|
75
|
+
- `excludeSessionPrefixes` skips sessions whose id starts with a configured prefix.
|
|
76
|
+
|
|
77
|
+
### Client (`lib/client.js`)
|
|
78
|
+
|
|
79
|
+
- Native settings card on `settings.plugin.item`.
|
|
80
|
+
- Web Audio API dual-tone chime synthesizer for `task_done`, `error`, and `approval_requested` with interactive "Test sound" button.
|
|
81
|
+
- Non-intrusive floating toast manager with session navigation button and interactive "Test toast" button.
|
|
82
|
+
- Native HTML5 desktop push integration with permission request workflow.
|
|
83
|
+
- Real-time SSE subscriber with exponential-backoff auto-reconnect and optional `notifyBackgroundOnly` filter.
|
|
84
|
+
- Snapshot status `loading` / `unavailable` / `ready` before the form is writable.
|
|
85
|
+
- Save writes every field and lists named failures.
|
|
86
|
+
- Locale dictionaries: `en` and `zh` only. Russian UI is supplied at runtime by `dsh-russian-lang`.
|
|
87
|
+
- Injected stylesheet is tagged `data-dsh-plugin="dsh-plugin-notify"`.
|
|
88
|
+
|
|
89
|
+
## Install
|
|
90
|
+
|
|
91
|
+
This package is private (GitHub Packages). After you have registry access:
|
|
92
|
+
|
|
93
|
+
```sh
|
|
94
|
+
dsh plugin --profile web add @goodandready/dsh-plugin-notify
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Restart the web profile so the client half loads. Then open **Settings → Plugins → Notify**.
|
|
98
|
+
|
|
99
|
+
## Configuration
|
|
100
|
+
|
|
101
|
+
Put each webhook URL into **Settings → Credentials**. In the plugin card, type only the credential name.
|
|
102
|
+
|
|
103
|
+
```yaml
|
|
104
|
+
- id: plugin-notify
|
|
105
|
+
name: '@goodandready/dsh-plugin-notify'
|
|
106
|
+
config:
|
|
107
|
+
enableSound: false
|
|
108
|
+
enableToasts: false
|
|
109
|
+
enableDesktopNotifications: false
|
|
110
|
+
notifyBackgroundOnly: false
|
|
111
|
+
webhooks:
|
|
112
|
+
feishu: NOTIFY_FEISHU_WEBHOOK
|
|
113
|
+
wecom: NOTIFY_WECOM_WEBHOOK
|
|
114
|
+
dingtalk: NOTIFY_DINGTALK_WEBHOOK
|
|
115
|
+
slack: NOTIFY_SLACK_WEBHOOK
|
|
116
|
+
discord: NOTIFY_DISCORD_WEBHOOK
|
|
117
|
+
custom: NOTIFY_CUSTOM_WEBHOOK
|
|
118
|
+
events: [task_done, error, approval_requested]
|
|
119
|
+
local: true
|
|
120
|
+
timeoutMs: 5000
|
|
121
|
+
dnd:
|
|
122
|
+
start: ''
|
|
123
|
+
end: ''
|
|
124
|
+
includeSession: true
|
|
125
|
+
includeDuration: true
|
|
126
|
+
excludeSessionPrefixes: []
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
| Parameter | Type | Default | Description |
|
|
130
|
+
|---|---|---|---|
|
|
131
|
+
| `enableSound` | boolean | `false` | Synthesize gentle Web Audio chimes on turn finish, error, or approval wait (opt-in). |
|
|
132
|
+
| `enableToasts` | boolean | `false` | Show cross-session on-screen banner toasts with interactive session switching (opt-in). |
|
|
133
|
+
| `enableDesktopNotifications` | boolean | `false` | Show native OS push notifications via HTML5 Notification API (opt-in). |
|
|
134
|
+
| `notifyBackgroundOnly` | boolean | `false` | Only trigger audio, toasts, and push when event is from an inactive/background session. |
|
|
135
|
+
| `webhooks.*` | string | empty | Credential **name** whose value is the webhook URL. Empty disables the channel. |
|
|
136
|
+
| `events` | string[] | `task_done`, `error`, `approval_requested` | Event whitelist. Empty restores the default three. |
|
|
137
|
+
| `local` | boolean | `true` | macOS `osascript` popup; ignored on other platforms. |
|
|
138
|
+
| `timeoutMs` | number | `5000` | Per-request abort timeout. |
|
|
139
|
+
| `dnd.start` / `dnd.end` | string | empty | `HH:MM` window. Equal or invalid values disable DND. |
|
|
140
|
+
| `includeSession` | boolean | `true` | Append `Session: …` to the text body. |
|
|
141
|
+
| `includeDuration` | boolean | `true` | Append `Duration: …` when a turn start timestamp is known. |
|
|
142
|
+
| `excludeSessionPrefixes` | string[] | `[]` | Skip notifications when `session.id` starts with any prefix. |
|
|
143
|
+
|
|
144
|
+
A leftover raw `http(s)://` value in `webhooks.*` still posts, with a deprecation warning. Migrate it to Credentials.
|
|
145
|
+
|
|
146
|
+
## Message shape
|
|
147
|
+
|
|
148
|
+
| Channel | JSON body |
|
|
149
|
+
|---|---|
|
|
150
|
+
| Feishu | `{ msg_type: 'text', content: { text } }` |
|
|
151
|
+
| WeCom | `{ msgtype: 'text', text: { content: text } }` |
|
|
152
|
+
| DingTalk | `{ msgtype: 'text', text: { content: text } }` |
|
|
153
|
+
| Slack | `{ text }` |
|
|
154
|
+
| Discord | `{ content: text }` |
|
|
155
|
+
| custom | `{ text, kind, title, sessionId, durationMs, time }` |
|
|
156
|
+
|
|
157
|
+
Text body:
|
|
158
|
+
|
|
159
|
+
```
|
|
160
|
+
【Task done】short session title
|
|
161
|
+
Summary: …
|
|
162
|
+
Reason: completed
|
|
163
|
+
Duration: 3m 42s
|
|
164
|
+
Session: session-1
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Tests
|
|
168
|
+
|
|
169
|
+
From a clone:
|
|
170
|
+
|
|
171
|
+
```sh
|
|
172
|
+
npm install --no-audit --no-fund --no-package-lock
|
|
173
|
+
npm test
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
`pretest` runs `node --check` on `lib/index.js` and `lib/client.js`. `npm test` then runs `node --test test/*.test.mjs`.
|
|
177
|
+
|
|
178
|
+
The suite stubs `fetch` / a local HTTP listener. It does not call a real IM provider. Live delivery needs a webhook you own.
|
|
179
|
+
|
|
180
|
+
Expected output:
|
|
181
|
+
|
|
182
|
+
```text
|
|
183
|
+
✔ public package identity matches host, client and patch sites
|
|
184
|
+
✔ client locale registration coexists with Russian language pack
|
|
185
|
+
✔ client apply does not register ru and can reload after effect dispose
|
|
186
|
+
✔ legacy raw webhook URL still posts (compat)
|
|
187
|
+
✔ credential ref resolves webhook URL via credentials service
|
|
188
|
+
✔ resolveWebhookValue prefers credentials then env
|
|
189
|
+
✔ missing credential name does not post
|
|
190
|
+
✔ each IM channel posts the expected body shape
|
|
191
|
+
✔ recipient HTTP failure does not throw out of the session loop
|
|
192
|
+
✔ AbortSignal.timeout is attached to webhook POST
|
|
193
|
+
✔ excluded session prefixes suppress notifications
|
|
194
|
+
✔ Config schema validates sound, toast, and desktop notification fields with opt-in defaults
|
|
195
|
+
✔ SSE route registers on webServer, rejects untrusted requests, and handles trusted stream
|
|
196
|
+
✔ client does not register settings.section slot (issue #16 fix)
|
|
197
|
+
✔ deliveries and warnings are routed through ctx.logger without console calls (issue #22 fix)
|
|
198
|
+
ℹ tests 15
|
|
199
|
+
ℹ pass 15
|
|
200
|
+
ℹ fail 0
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## License
|
|
204
|
+
|
|
205
|
+
MIT © [GooDAnDReaDY](https://github.com/GooDAnDReaDY)
|
|
206
|
+
|
|
207
|
+
## Changelog
|
|
208
|
+
|
|
209
|
+
See [CHANGELOG.md](CHANGELOG.md) for full release and version history.
|
|
210
|
+
|
package/README.ru.md
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# 📦 @goodandready/dsh-plugin-notify
|
|
2
|
+
|
|
3
|
+
<div align="center">
|
|
4
|
+
|
|
5
|
+
<h3>Удалённые IM-webhook уведомления о завершении хода, ошибке и ожидании approval</h3>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-10b981.svg?style=for-the-badge&labelColor=064e3b" alt="license"></a>
|
|
9
|
+
<a href="https://github.com/topics/dsh-plugin"><img src="https://img.shields.io/badge/DSH-Plugin-8b5cf6.svg?style=for-the-badge&labelColor=2e1065" alt="DSH Plugin"></a>
|
|
10
|
+
<a href="https://nodejs.org"><img src="https://img.shields.io/badge/Node-20%2B-f59e0b.svg?style=for-the-badge&labelColor=451a03" alt="Node version"></a>
|
|
11
|
+
</p>
|
|
12
|
+
|
|
13
|
+
<p align="center">
|
|
14
|
+
<a href="https://goodandready.app/"><img src="https://img.shields.io/badge/🌐_DSH_Hub-goodandready.app-ff4500.svg?style=for-the-badge&labelColor=1a1a2e" alt="GoodAndReady Showcase"></a>
|
|
15
|
+
</p>
|
|
16
|
+
|
|
17
|
+
<p align="center">
|
|
18
|
+
<a href="README.md"><b>🇬🇧 English</b></a> •
|
|
19
|
+
<a href="README.zh.md"><b>🇨🇳 中文说明</b></a> •
|
|
20
|
+
<a href="README.ru.md"><b>🇷🇺 Русский</b></a>
|
|
21
|
+
</p>
|
|
22
|
+
|
|
23
|
+
<table align="center">
|
|
24
|
+
<tr>
|
|
25
|
+
<td align="center">
|
|
26
|
+
⭐ <strong>Если вам нравится этот плагин, поставьте ему Star на GitHub</strong> — это покажет мне, что плагин полезен, и добавит мотивации продолжать его развитие.
|
|
27
|
+
<br><br>
|
|
28
|
+
🐛 <strong>Если вы нашли баг или хотите предложить новую функцию</strong>, создайте Issue на GitHub на любом языке — я рассмотрю предложение и реализую полезные улучшения в одной из следующих версий плагина.
|
|
29
|
+
</td>
|
|
30
|
+
</tr>
|
|
31
|
+
</table>
|
|
32
|
+
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Обзор / Проблема
|
|
38
|
+
|
|
39
|
+
DeepSeek Harness уже знает, когда ход завершился, упал или ждёт approval. Без этого плагина события остаются внутри сессии. Если вы ведёте несколько параллельных сессий или переключились в другое приложение, приходится постоянно заглядывать и проверять статус вручную.
|
|
40
|
+
|
|
41
|
+
Плагин закрывает этот разрыв четырьмя уровнями уведомлений:
|
|
42
|
+
1. **Звуковые сигналы (Web Audio)**: Приятные пентатонические переливы колокольчиков прямо в браузере или приложении DSH без внешних аудиофайлов.
|
|
43
|
+
2. **Экранные тосты (In-App Toasts)**: Всплывающие карточки поверх сессий с интерактивной кнопкой **«Перейти в сессию»** для мгновенного перехода к нужной задаче.
|
|
44
|
+
3. **Системные уведомления (OS / Desktop Push)**: Нативные уведомления Windows, macOS, Linux и DSH Desktop через HTML5 `Notification API` с фокусировкой окна и переходом в сессию.
|
|
45
|
+
4. **Удалённые вебхуки (Remote IM)**: Отправка JSON-событий в Feishu, WeCom, DingTalk, Slack, Discord или custom HTTP-эндпоинт. Секретные URL вебхуков надёжно хранятся в DSH Credentials.
|
|
46
|
+
|
|
47
|
+
Русский интерфейс карточки настроек даёт отдельный языковой пакет `dsh-russian-lang`. Этот плагин регистрирует словари `en` и `zh`.
|
|
48
|
+
|
|
49
|
+
## Архитектура
|
|
50
|
+
|
|
51
|
+
```mermaid
|
|
52
|
+
graph TD
|
|
53
|
+
A[DSH session/event] --> B[plugin-notify host]
|
|
54
|
+
B -->|credential name| C[Credentials service]
|
|
55
|
+
C -->|webhook URL| B
|
|
56
|
+
B -->|POST JSON| D[Feishu / WeCom / DingTalk / Slack / Discord / custom]
|
|
57
|
+
B -.->|macOS only| E[osascript notification]
|
|
58
|
+
B -->|SSE stream: /dsh-plugin-notify/events| F[Client Listener lib/client.js]
|
|
59
|
+
F -->|Web Audio API| G[Звуковые сигналы]
|
|
60
|
+
F -->|DOM overlay| H[Экранные тосты]
|
|
61
|
+
F -->|Notification API| I[Десктопные уведомления OS]
|
|
62
|
+
J[Карточка настроек] -->|параметры| B
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Возможности
|
|
66
|
+
|
|
67
|
+
### Хост (`lib/index.js`)
|
|
68
|
+
|
|
69
|
+
- Подписка на `session/event`.
|
|
70
|
+
- `turn/end` с `reason.kind === 'completed'` → `task_done`.
|
|
71
|
+
- любой другой `turn/end` → `error`.
|
|
72
|
+
- `approval/asked` → `approval_requested`.
|
|
73
|
+
- Трансляция событий в реальном времени через SSE (`GET /dsh-plugin-notify/events`) через сервис Cordis `webServer`.
|
|
74
|
+
- Разрешение `webhooks.*`: устаревший сырой `http(s)://` (предупреждение) → Credentials `resolve` → `process.env[name]`.
|
|
75
|
+
- POST с `AbortSignal.timeout(timeoutMs)` (по умолчанию 5000 мс). Ошибка POST только логируется, без ретрая и без блокировки цикла агента.
|
|
76
|
+
- Опциональное окно DND (`HH:MM`, в том числе через полночь). События наблюдаются; вебхуки, звуки и локальные попапы пропускаются.
|
|
77
|
+
- `excludeSessionPrefixes` пропускает сессии, чей id начинается с заданного префикса.
|
|
78
|
+
|
|
79
|
+
### Клиент (`lib/client.js`)
|
|
80
|
+
|
|
81
|
+
- Нативная карточка настроек на `settings.plugin.item`.
|
|
82
|
+
- Синтезатор звуковых сигналов Web Audio API для `task_done`, `error` и `approval_requested` с кнопкой «Проверить звук».
|
|
83
|
+
- Ненавязчивый менеджер тостов с кнопкой перехода в сессию и кнопкой «Проверить тост».
|
|
84
|
+
- Поддержка нативных десктопных пушей HTML5 с запросом разрешений.
|
|
85
|
+
- Подписчик SSE с авто-реконнектом и опциональным фильтром `notifyBackgroundOnly`.
|
|
86
|
+
- Статусы снимка `loading` / `unavailable` / `ready`.
|
|
87
|
+
- Save записывает все поля и перечисляет ошибки по имени.
|
|
88
|
+
- Стили помечаются `data-dsh-plugin="dsh-plugin-notify"`.
|
|
89
|
+
|
|
90
|
+
## Установка
|
|
91
|
+
|
|
92
|
+
Пакет приватный (GitHub Packages). После доступа к registry:
|
|
93
|
+
|
|
94
|
+
```sh
|
|
95
|
+
dsh plugin --profile web add @goodandready/dsh-plugin-notify
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Перезапустите web-профиль, чтобы загрузилась клиентская половина. Затем **Настройки → Плагины → Notify**.
|
|
99
|
+
|
|
100
|
+
## Конфигурация
|
|
101
|
+
|
|
102
|
+
Положите каждый webhook URL в **Настройки → Credentials**. В карточке плагина указывайте только имя учётной записи.
|
|
103
|
+
|
|
104
|
+
```yaml
|
|
105
|
+
- id: plugin-notify
|
|
106
|
+
name: '@goodandready/dsh-plugin-notify'
|
|
107
|
+
config:
|
|
108
|
+
enableSound: false
|
|
109
|
+
enableToasts: false
|
|
110
|
+
enableDesktopNotifications: false
|
|
111
|
+
notifyBackgroundOnly: false
|
|
112
|
+
webhooks:
|
|
113
|
+
feishu: NOTIFY_FEISHU_WEBHOOK
|
|
114
|
+
wecom: NOTIFY_WECOM_WEBHOOK
|
|
115
|
+
dingtalk: NOTIFY_DINGTALK_WEBHOOK
|
|
116
|
+
slack: NOTIFY_SLACK_WEBHOOK
|
|
117
|
+
discord: NOTIFY_DISCORD_WEBHOOK
|
|
118
|
+
custom: NOTIFY_CUSTOM_WEBHOOK
|
|
119
|
+
events: [task_done, error, approval_requested]
|
|
120
|
+
local: true
|
|
121
|
+
timeoutMs: 5000
|
|
122
|
+
dnd:
|
|
123
|
+
start: ''
|
|
124
|
+
end: ''
|
|
125
|
+
includeSession: true
|
|
126
|
+
includeDuration: true
|
|
127
|
+
excludeSessionPrefixes: []
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
| Параметр | Тип | По умолчанию | Описание |
|
|
131
|
+
|---|---|---|---|
|
|
132
|
+
| `enableSound` | boolean | `false` | Звуковые сигналы Web Audio при завершении задачи, ошибке или запросе approval (выключено по умолчанию, opt-in). |
|
|
133
|
+
| `enableToasts` | boolean | `false` | Экранные всплывающие тосты поверх всех сессий с кнопкой перехода (opt-in). |
|
|
134
|
+
| `enableDesktopNotifications` | boolean | `false` | Системные десктопные уведомления ОС через Notification API (opt-in). |
|
|
135
|
+
| `notifyBackgroundOnly` | boolean | `false` | Уведомлять только если событие произошло в неактивной фоновой сессии. |
|
|
136
|
+
| `webhooks.*` | string | пусто | **Имя** credential, значение которого — URL вебхука. Пусто отключает канал. |
|
|
137
|
+
| `events` | string[] | `task_done`, `error`, `approval_requested` | Белый список событий. Пусто возвращает три значения по умолчанию. |
|
|
138
|
+
| `local` | boolean | `true` | macOS `osascript`; на других ОС игнорируется. |
|
|
139
|
+
| `timeoutMs` | number | `5000` | Таймаут одного запроса. |
|
|
140
|
+
| `dnd.start` / `dnd.end` | string | пусто | Окно `HH:MM`. Равные или невалидные значения отключают DND. |
|
|
141
|
+
| `includeSession` | boolean | `true` | Добавить строку `Session: …`. |
|
|
142
|
+
| `includeDuration` | boolean | `true` | Добавить `Duration: …`, если известно время старта хода. |
|
|
143
|
+
| `excludeSessionPrefixes` | string[] | `[]` | Не слать уведомления, если `session.id` начинается с префикса. |
|
|
144
|
+
|
|
145
|
+
Сырой `http(s)://` в `webhooks.*` всё ещё отправляется с предупреждением. Перенесите URL в Credentials.
|
|
146
|
+
|
|
147
|
+
## Формат сообщения
|
|
148
|
+
|
|
149
|
+
| Канал | JSON |
|
|
150
|
+
|---|---|
|
|
151
|
+
| Feishu | `{ msg_type: 'text', content: { text } }` |
|
|
152
|
+
| WeCom | `{ msgtype: 'text', text: { content: text } }` |
|
|
153
|
+
| DingTalk | `{ msgtype: 'text', text: { content: text } }` |
|
|
154
|
+
| Slack | `{ text }` |
|
|
155
|
+
| Discord | `{ content: text }` |
|
|
156
|
+
| custom | `{ text, kind, title, sessionId, durationMs, time }` |
|
|
157
|
+
|
|
158
|
+
## Тесты
|
|
159
|
+
|
|
160
|
+
```sh
|
|
161
|
+
npm install --no-audit --no-fund --no-package-lock
|
|
162
|
+
npm test
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
`pretest` делает `node --check` для `lib/index.js` и `lib/client.js`. Затем `node --test test/*.test.mjs`.
|
|
166
|
+
|
|
167
|
+
Набор подменяет `fetch` или поднимает локальный HTTP-приёмник и не ходит в реальный IM. Живая доставка требует вашего вебхука.
|
|
168
|
+
|
|
169
|
+
Ожидаемый вывод:
|
|
170
|
+
|
|
171
|
+
```text
|
|
172
|
+
✔ public package identity matches host, client and patch sites
|
|
173
|
+
✔ client locale registration coexists with Russian language pack
|
|
174
|
+
✔ client apply does not register ru and can reload after effect dispose
|
|
175
|
+
✔ legacy raw webhook URL still posts (compat)
|
|
176
|
+
✔ credential ref resolves webhook URL via credentials service
|
|
177
|
+
✔ resolveWebhookValue prefers credentials then env
|
|
178
|
+
✔ missing credential name does not post
|
|
179
|
+
✔ each IM channel posts the expected body shape
|
|
180
|
+
✔ recipient HTTP failure does not throw out of the session loop
|
|
181
|
+
✔ AbortSignal.timeout is attached to webhook POST
|
|
182
|
+
✔ excluded session prefixes suppress notifications
|
|
183
|
+
✔ Config schema validates sound, toast, and desktop notification fields with opt-in defaults
|
|
184
|
+
✔ SSE route registers on webServer, rejects untrusted requests, and handles trusted stream
|
|
185
|
+
✔ client does not register settings.section slot (issue #16 fix)
|
|
186
|
+
✔ deliveries and warnings are routed through ctx.logger without console calls (issue #22 fix)
|
|
187
|
+
ℹ tests 15
|
|
188
|
+
ℹ pass 15
|
|
189
|
+
ℹ fail 0
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## Лицензия
|
|
193
|
+
|
|
194
|
+
MIT © [GooDAnDReaDY](https://github.com/GooDAnDReaDY)
|
|
195
|
+
|
|
196
|
+
## История изменений
|
|
197
|
+
|
|
198
|
+
Подробный список изменений доступен в [CHANGELOG.md](CHANGELOG.md).
|
|
199
|
+
|