dsh-notify-windows 0.7.8 → 0.8.0-beta.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 +30 -0
- package/README.en.md +38 -0
- package/README.md +35 -0
- package/lib/index.js +146 -18
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,36 @@
|
|
|
2
2
|
|
|
3
3
|
本项目遵循 [Semantic Versioning](https://semver.org/)。
|
|
4
4
|
|
|
5
|
+
## [Unreleased]
|
|
6
|
+
|
|
7
|
+
### 新增
|
|
8
|
+
|
|
9
|
+
- **多语言 toast 文案(`locale` / `messages`)**:新增内置语言包 `zh`(默认,**保持现有文案不变**)、
|
|
10
|
+
`en`、`ru`,并由 `locale` 选择;任意一条文案都可用 `messages` 按 key 覆盖,覆盖优先于语言包。
|
|
11
|
+
占位符(`{turn}` / `{tool}` / `{reason}` / `{version}` / `{code}`)由事件数据代入:未知占位符
|
|
12
|
+
**原样保留**而非清空,非字符串覆盖值被忽略,未知 `locale` 回退默认语言包——写错不会产生空正文或
|
|
13
|
+
`[object Object]`。默认值不变,故本次改动对既有安装零影响(升级后文案与行为完全一致)。
|
|
14
|
+
- 审批(`approval`)与提问(`ask-user`)路径的日志行补充 `title` 与 `body` 字段,与既有的
|
|
15
|
+
`notify` 行对齐,便于排查「实际发出去的文案是什么」。新增字段为**增量**,不破坏既有日志消费方。
|
|
16
|
+
|
|
17
|
+
### 测试
|
|
18
|
+
|
|
19
|
+
- `test/probe.test.mjs` 新增 5 个用例(共 20/20 通过):
|
|
20
|
+
- `resolveMessages`:各语言包**键集合一致**、每条文案非空、`{turn}` / `{tool}` / `{reason}` /
|
|
21
|
+
`{version}` / `{code}` 占位符**逐项齐备**(漏一个即变红)、未知 `locale` 回退默认包、
|
|
22
|
+
`messages` 覆盖优先于语言包、非字符串覆盖被忽略、`messages` 非对象不崩;
|
|
23
|
+
- 端到端 `locale=en` / `locale=ru`:完成回合正文与轮次后缀(`Task completed (turn 1)` /
|
|
24
|
+
`Задача завершена (раунд 1)`);
|
|
25
|
+
- 端到端 `messages` 覆盖:未覆盖的键仍用所选语言包;
|
|
26
|
+
- 端到端 approval / ask 的标题与正文,含 `approvalGeneric` 与 `askFallback` 两条兜底文案。
|
|
27
|
+
|
|
28
|
+
### 修复
|
|
29
|
+
|
|
30
|
+
- `resolveMessages` 的 locale 查找改用 `Object.hasOwn`:`locale` 若恰为 `Object.prototype` 成员名
|
|
31
|
+
(`constructor` / `toString` / `__proto__` / `hasOwnProperty` / `valueOf`),裸索引会取到函数、
|
|
32
|
+
展开成空包,正文渲染成字面量 `undefined` —— 与「未知 locale 回退默认包」的承诺不符。
|
|
33
|
+
现在与其它未知 locale 一样回退 `zh`。
|
|
34
|
+
|
|
5
35
|
## [0.7.8] - 2026-09-29
|
|
6
36
|
|
|
7
37
|
### 兼容性
|
package/README.en.md
CHANGED
|
@@ -72,6 +72,8 @@ in `$DSH_HOME/profiles/web/cordis.patch.yml` (hot-applied by the running host
|
|
|
72
72
|
| --- | --- | --- |
|
|
73
73
|
| `enabled` | `true` | master switch |
|
|
74
74
|
| `reasons` | `[completed, error, max-tokens]` | turn-end reasons to notify (`aborted` / `interrupted` / `blocked` also available) |
|
|
75
|
+
| `locale` | `zh` | built-in message pack for the toast text: `zh` / `en` / `ru` |
|
|
76
|
+
| `messages` | `{}` | per-key wording overrides applied on top of `locale` (see Localization) |
|
|
75
77
|
| `includeSubagents` | `false` | also notify subagent sessions |
|
|
76
78
|
| `notifyOnStart` | `false` | toast when the plugin loads |
|
|
77
79
|
| `notifyOnApproval` | `true` | toast on `approval/asked` (skipped under policy `never`) |
|
|
@@ -87,6 +89,42 @@ in `$DSH_HOME/profiles/web/cordis.patch.yml` (hot-applied by the running host
|
|
|
87
89
|
| `preferExisting` | `true` | when true, launcher prefers focusing an open DSH window; when false, just open the URL in the default browser |
|
|
88
90
|
| `webUrl` | `''` | override the auto-discovered base URL (e.g. `http://192.168.1.5:4000`); leave empty to auto-discover (defaults to `http://127.0.0.1:3080`) |
|
|
89
91
|
|
|
92
|
+
## 🌐 Localization
|
|
93
|
+
|
|
94
|
+
Toast wording comes from a built-in message pack selected by `locale`. The default is `zh`, so an
|
|
95
|
+
existing install sees no change after upgrading; `en` and `ru` ship as complete packs. Any single
|
|
96
|
+
string can be overridden per install through `messages`, which wins over the selected pack:
|
|
97
|
+
|
|
98
|
+
```yaml
|
|
99
|
+
- id: dsh-notify
|
|
100
|
+
name: dsh-notify-windows
|
|
101
|
+
config:
|
|
102
|
+
locale: ru
|
|
103
|
+
messages:
|
|
104
|
+
approvalTitle: ' · Требуется одобрение'
|
|
105
|
+
turnSuffix: ' — попытка {turn}'
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
| Key | Used for |
|
|
109
|
+
| --- | --- |
|
|
110
|
+
| `completed` / `error` / `max-tokens` / `aborted` / `interrupted` / `blocked` | the turn/end reason line (looked up by `reason.kind`) |
|
|
111
|
+
| `turnEnded` | fallback when a reason has no entry in the pack |
|
|
112
|
+
| `turnSuffix` | turn number appended to the reason line — `{turn}` |
|
|
113
|
+
| `approvalTitle` | title suffix of an approval toast |
|
|
114
|
+
| `approvalToolWithReason` / `approvalTool` / `approvalGeneric` | approval body — `{tool}`, `{reason}` |
|
|
115
|
+
| `askTitle` | title suffix of a question toast |
|
|
116
|
+
| `askFallback` | question body when no question text could be extracted |
|
|
117
|
+
| `activated` | the `notifyOnStart` toast — `{version}` |
|
|
118
|
+
| `warnSpawnFailed` / `warnToastFailed` / `warnExitCode` / `warnChannelUnavailable` | DSH log wording — `{code}`; the `dsh-notify:` tag itself is never translated, so logs stay greppable across locales |
|
|
119
|
+
|
|
120
|
+
`{placeholder}` tokens are substituted from the event data. An unknown placeholder is left verbatim
|
|
121
|
+
rather than blanked, and a non-string override is ignored — a typo can therefore never produce an
|
|
122
|
+
empty or `[object Object]` toast. An unknown `locale` falls back to the default pack.
|
|
123
|
+
|
|
124
|
+
The `approval` and `ask-user` log entries now carry `title` and `body` alongside the existing
|
|
125
|
+
metadata (matching what the `notify` entry already did), so `%TEMP%\dsh-notify\notify.log` shows
|
|
126
|
+
the text that was actually sent.
|
|
127
|
+
|
|
90
128
|
## 🔔 Triggers
|
|
91
129
|
|
|
92
130
|
| Scenario | Session event | Sample toast |
|
package/README.md
CHANGED
|
@@ -74,6 +74,8 @@ dsh plugin --profile web remove dsh-notify-windows
|
|
|
74
74
|
| --- | --- | --- |
|
|
75
75
|
| `enabled` | `true` | 总开关 |
|
|
76
76
|
| `reasons` | `[completed, error, max-tokens]` | 需要提醒的回合结束原因(可选 `aborted` / `interrupted` / `blocked`) |
|
|
77
|
+
| `locale` | `zh` | toast 文案的内置语言包:`zh` / `en` / `ru` |
|
|
78
|
+
| `messages` | `{}` | 按 key 覆盖文案,优先级高于 `locale`(见「多语言」) |
|
|
77
79
|
| `includeSubagents` | `false` | 是否也提醒子代理会话 |
|
|
78
80
|
| `notifyOnStart` | `false` | 插件加载时发一条「已激活」通知 |
|
|
79
81
|
| `notifyOnApproval` | `true` | 审批请求提醒(策略 `never` 时自动跳过) |
|
|
@@ -89,6 +91,39 @@ dsh plugin --profile web remove dsh-notify-windows
|
|
|
89
91
|
| `preferExisting` | `true` | true 走 launcher 优先聚焦已开 DSH 窗口;false 直接用默认浏览器打开 URL |
|
|
90
92
|
| `webUrl` | `''` | 覆盖自动发现的基地址(如 `http://192.168.1.5:4000`),留空自动发现(默认 `http://127.0.0.1:3080`) |
|
|
91
93
|
|
|
94
|
+
## 🌐 多语言
|
|
95
|
+
|
|
96
|
+
toast 文案取自内置语言包,由 `locale` 选择。默认 `zh`,因此已有安装升级后文案完全不变;
|
|
97
|
+
`en` 与 `ru` 为完整语言包。任何一条文案都可以用 `messages` 按 key 覆盖,覆盖优先于语言包:
|
|
98
|
+
|
|
99
|
+
```yaml
|
|
100
|
+
- id: dsh-notify
|
|
101
|
+
name: dsh-notify-windows
|
|
102
|
+
config:
|
|
103
|
+
locale: ru
|
|
104
|
+
messages:
|
|
105
|
+
approvalTitle: ' · Требуется одобрение'
|
|
106
|
+
turnSuffix: ' — попытка {turn}'
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
| key | 用途 |
|
|
110
|
+
| --- | --- |
|
|
111
|
+
| `completed` / `error` / `max-tokens` / `aborted` / `interrupted` / `blocked` | 回合结束原因行(按 `reason.kind` 查表) |
|
|
112
|
+
| `turnEnded` | 原因在语言包中没有对应条目时的兜底文案 |
|
|
113
|
+
| `turnSuffix` | 追加在原因行后的轮次后缀 —— `{turn}` |
|
|
114
|
+
| `approvalTitle` | 审批通知的标题后缀 |
|
|
115
|
+
| `approvalToolWithReason` / `approvalTool` / `approvalGeneric` | 审批正文 —— `{tool}` / `{reason}` |
|
|
116
|
+
| `askTitle` | 提问通知的标题后缀 |
|
|
117
|
+
| `askFallback` | 提取不到问题文本时的提问正文 |
|
|
118
|
+
| `activated` | `notifyOnStart` 的「已激活」通知 —— `{version}` |
|
|
119
|
+
| `warnSpawnFailed` / `warnToastFailed` / `warnExitCode` / `warnChannelUnavailable` | DSH 日志文案 —— `{code}`;`dsh-notify:` 前缀不翻译,保证跨语言可 grep |
|
|
120
|
+
|
|
121
|
+
说明:`{占位符}` 由事件数据代入;未知占位符**原样保留**(不会被清空),非字符串的覆盖值会被忽略,
|
|
122
|
+
因此写错不会产生空正文或 `[object Object]`;未知 `locale` 回退默认语言包。
|
|
123
|
+
|
|
124
|
+
审批 / 提问路径的日志行新增 `title` 与 `body` 字段(与 `notify` 行一致),
|
|
125
|
+
`%TEMP%\dsh-notify\notify.log` 因此能直接看到实际发出的文案。
|
|
126
|
+
|
|
92
127
|
## 🔔 触发场景
|
|
93
128
|
|
|
94
129
|
| 场景 | 会话事件 | 通知示例 |
|
package/lib/index.js
CHANGED
|
@@ -15,6 +15,11 @@
|
|
|
15
15
|
// Goal rounds (/goal auto-continuation) are quiet by default: only the final
|
|
16
16
|
// round that completes or blocks the goal produces a toast.
|
|
17
17
|
//
|
|
18
|
+
// Wording is localizable: `locale` selects a built-in message pack (MESSAGES —
|
|
19
|
+
// "zh" by default, "en" and "ru" also ship) and `messages` overrides individual
|
|
20
|
+
// strings on top of it. The default pack keeps the original text, so an install
|
|
21
|
+
// that sets neither key behaves exactly as before.
|
|
22
|
+
//
|
|
18
23
|
// Zero runtime dependencies: notifications go through the toast sender
|
|
19
24
|
// script (./notify.ps1) executed by Windows PowerShell 5.1, whose .NET
|
|
20
25
|
// Framework runtime supports the WinRT toast API.
|
|
@@ -54,15 +59,128 @@ const WSL_POWERSHELL = "/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershel
|
|
|
54
59
|
const resolvePowerShell = () => (existsSync(WSL_POWERSHELL) ? WSL_POWERSHELL : "powershell.exe");
|
|
55
60
|
const POWERSHELL = resolvePowerShell();
|
|
56
61
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
/**
|
|
63
|
+
* Built-in toast message packs.
|
|
64
|
+
*
|
|
65
|
+
* The keys serve two purposes at once. The turn/end reason kinds (`completed`,
|
|
66
|
+
* `error`, `max-tokens`, `aborted`, `interrupted`, `blocked`) are looked up
|
|
67
|
+
* directly by `reason.kind`, so every pack must carry one entry per reason;
|
|
68
|
+
* the remaining keys hold the surrounding toast and log wording.
|
|
69
|
+
*
|
|
70
|
+
* `zh` is the original wording and stays the default, so an existing install
|
|
71
|
+
* sees no change after upgrading. `en` and `ru` are complete packs. Any pack
|
|
72
|
+
* can be partially overridden per install through the `messages` config.
|
|
73
|
+
* `{placeholder}` tokens are substituted by `format()`.
|
|
74
|
+
*/
|
|
75
|
+
export const MESSAGES = {
|
|
76
|
+
zh: {
|
|
77
|
+
// turn/end reason kinds
|
|
78
|
+
completed: "任务已完成",
|
|
79
|
+
error: "任务出错",
|
|
80
|
+
"max-tokens": "输出达到 token 上限",
|
|
81
|
+
aborted: "任务已取消",
|
|
82
|
+
interrupted: "任务中断",
|
|
83
|
+
blocked: "任务未开始",
|
|
84
|
+
// turn/end wording
|
|
85
|
+
turnEnded: "任务已结束",
|
|
86
|
+
turnSuffix: "(第 {turn} 轮)",
|
|
87
|
+
// approval/asked wording
|
|
88
|
+
approvalTitle: " · 需要审批",
|
|
89
|
+
approvalToolWithReason: "工具 {tool}:{reason}",
|
|
90
|
+
approvalTool: "工具 {tool}",
|
|
91
|
+
approvalGeneric: "有一项操作等待你的批准",
|
|
92
|
+
// ask_user_question wording
|
|
93
|
+
askTitle: " · 需要回答",
|
|
94
|
+
askFallback: "Agent 正在等待你的确认",
|
|
95
|
+
// activation toast
|
|
96
|
+
activated: "任务完成提醒已激活(v{version})",
|
|
97
|
+
// logger messages (the "dsh-notify:" tag stays untranslated, so logs
|
|
98
|
+
// remain greppable across locales)
|
|
99
|
+
warnSpawnFailed: "启动通知进程失败",
|
|
100
|
+
warnToastFailed: "通知进程未成功显示 toast",
|
|
101
|
+
warnExitCode: "(退出码 {code})",
|
|
102
|
+
warnChannelUnavailable: "通知通道不可用(powershell.exe 或 notify.ps1 缺失),桌面通知将不会发送",
|
|
103
|
+
},
|
|
104
|
+
en: {
|
|
105
|
+
completed: "Task completed",
|
|
106
|
+
error: "Task errored",
|
|
107
|
+
"max-tokens": "Output reached the token cap",
|
|
108
|
+
aborted: "Task cancelled",
|
|
109
|
+
interrupted: "Task interrupted",
|
|
110
|
+
blocked: "Task not started",
|
|
111
|
+
turnEnded: "Task ended",
|
|
112
|
+
turnSuffix: " (turn {turn})",
|
|
113
|
+
approvalTitle: " · Needs approval",
|
|
114
|
+
approvalToolWithReason: "tool {tool}: {reason}",
|
|
115
|
+
approvalTool: "tool {tool}",
|
|
116
|
+
approvalGeneric: "An operation is waiting for your approval",
|
|
117
|
+
askTitle: " · Needs your answer",
|
|
118
|
+
askFallback: "The agent is waiting for your confirmation",
|
|
119
|
+
activated: "Task-completion reminders activated (v{version})",
|
|
120
|
+
warnSpawnFailed: "failed to start the notification process",
|
|
121
|
+
warnToastFailed: "the notification process did not show a toast",
|
|
122
|
+
warnExitCode: " (exit code {code})",
|
|
123
|
+
warnChannelUnavailable: "notification channel unavailable (powershell.exe or notify.ps1 missing); desktop toasts will not be sent",
|
|
124
|
+
},
|
|
125
|
+
ru: {
|
|
126
|
+
completed: "Задача завершена",
|
|
127
|
+
error: "Ошибка в задаче",
|
|
128
|
+
"max-tokens": "Достигнут лимит токенов",
|
|
129
|
+
aborted: "Задача отменена",
|
|
130
|
+
interrupted: "Задача прервана",
|
|
131
|
+
blocked: "Задача не начата",
|
|
132
|
+
turnEnded: "Задача завершена",
|
|
133
|
+
turnSuffix: " (раунд {turn})",
|
|
134
|
+
approvalTitle: " · Требуется одобрение",
|
|
135
|
+
approvalToolWithReason: "Инструмент {tool}: {reason}",
|
|
136
|
+
approvalTool: "Инструмент {tool}",
|
|
137
|
+
approvalGeneric: "Одна операция ожидает вашего одобрения",
|
|
138
|
+
askTitle: " · Требуется ответ",
|
|
139
|
+
askFallback: "Агент ждёт вашего подтверждения",
|
|
140
|
+
activated: "Напоминания о задачах активированы (v{version})",
|
|
141
|
+
warnSpawnFailed: "не удалось запустить процесс уведомления",
|
|
142
|
+
warnToastFailed: "не удалось показать тост",
|
|
143
|
+
warnExitCode: " (код выхода {code})",
|
|
144
|
+
warnChannelUnavailable: "канал уведомлений недоступен (нет powershell.exe или notify.ps1); тосты отправляться не будут",
|
|
145
|
+
},
|
|
64
146
|
};
|
|
65
147
|
|
|
148
|
+
/**
|
|
149
|
+
* Substitute `{name}` tokens with `params.name`. An unknown token is left
|
|
150
|
+
* verbatim rather than blanked, so a typo in an override stays visible in the
|
|
151
|
+
* toast instead of silently dropping text.
|
|
152
|
+
*/
|
|
153
|
+
function format(template, params) {
|
|
154
|
+
if (typeof template !== "string") return "";
|
|
155
|
+
return template.replace(/\{(\w+)\}/g, (match, key) =>
|
|
156
|
+
Object.prototype.hasOwnProperty.call(params, key) ? String(params[key]) : match,
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Resolve the effective message pack: the selected locale (an unknown locale
|
|
162
|
+
* falls back to the default pack rather than leaving the toast empty), then
|
|
163
|
+
* the install's own `messages` overrides on top.
|
|
164
|
+
*
|
|
165
|
+
* A non-string override is ignored: it could only ever produce "[object
|
|
166
|
+
* Object]" in a toast, and silently dropping it is better than leaking it.
|
|
167
|
+
*/
|
|
168
|
+
export function resolveMessages(config = {}) {
|
|
169
|
+
// An unknown locale falls back to the default pack. `Object.hasOwn` (not a
|
|
170
|
+
// bare index) is required: a locale naming an Object.prototype member
|
|
171
|
+
// ("constructor", "toString", "__proto__", …) would otherwise resolve to a
|
|
172
|
+
// function, spread into an empty pack, and render a literal "undefined" body.
|
|
173
|
+
const base = Object.hasOwn(MESSAGES, config.locale) ? MESSAGES[config.locale] : MESSAGES.zh;
|
|
174
|
+
const resolved = { ...base };
|
|
175
|
+
const overrides = config.messages;
|
|
176
|
+
if (overrides && typeof overrides === "object") {
|
|
177
|
+
for (const [key, value] of Object.entries(overrides)) {
|
|
178
|
+
if (typeof value === "string") resolved[key] = value;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return resolved;
|
|
182
|
+
}
|
|
183
|
+
|
|
66
184
|
export const name = "dsh-notify-windows";
|
|
67
185
|
|
|
68
186
|
function truncate(text, max) {
|
|
@@ -195,6 +313,8 @@ export const apply = (ctx, config = {}) => {
|
|
|
195
313
|
const cfg = {
|
|
196
314
|
enabled: true,
|
|
197
315
|
reasons: ["completed", "error", "max-tokens"],
|
|
316
|
+
locale: "zh", // built-in pack: "zh" (default) | "en" | "ru"
|
|
317
|
+
messages: {}, // per-key overrides applied on top of the locale pack
|
|
198
318
|
includeSubagents: false,
|
|
199
319
|
notifyOnStart: false,
|
|
200
320
|
notifyOnApproval: true,
|
|
@@ -213,6 +333,7 @@ export const apply = (ctx, config = {}) => {
|
|
|
213
333
|
...config,
|
|
214
334
|
};
|
|
215
335
|
const reasons = new Set(cfg.reasons);
|
|
336
|
+
const t = resolveMessages(cfg);
|
|
216
337
|
const logPath = join(tmpdir(), "dsh-notify", "notify.log");
|
|
217
338
|
|
|
218
339
|
const log = (entry) => {
|
|
@@ -284,7 +405,7 @@ export const apply = (ctx, config = {}) => {
|
|
|
284
405
|
child.stderr?.on("data", (chunk) => { stderr += chunk; });
|
|
285
406
|
child.on("error", (error) => {
|
|
286
407
|
log({ event: "error", message: String(error?.message ?? error) });
|
|
287
|
-
ctx.logger?.warn?.("dsh-notify:
|
|
408
|
+
ctx.logger?.warn?.("dsh-notify: " + t.warnSpawnFailed, error);
|
|
288
409
|
});
|
|
289
410
|
child.on("exit", (code) => {
|
|
290
411
|
const shown = /toast shown/.test(stdout);
|
|
@@ -296,7 +417,7 @@ export const apply = (ctx, config = {}) => {
|
|
|
296
417
|
stdout: stdout.slice(0, 500),
|
|
297
418
|
stderr: stderr.slice(0, 500),
|
|
298
419
|
});
|
|
299
|
-
ctx.logger?.warn?.("dsh-notify:
|
|
420
|
+
ctx.logger?.warn?.("dsh-notify: " + t.warnToastFailed + (code !== null ? format(t.warnExitCode, { code }) : ""));
|
|
300
421
|
}
|
|
301
422
|
});
|
|
302
423
|
};
|
|
@@ -354,8 +475,8 @@ export const apply = (ctx, config = {}) => {
|
|
|
354
475
|
}
|
|
355
476
|
|
|
356
477
|
const title = titleFor(session) || cfg.appName;
|
|
357
|
-
let body =
|
|
358
|
-
if (Number.isInteger(event.data.turn)) body +=
|
|
478
|
+
let body = t[reason.kind] ?? t.turnEnded;
|
|
479
|
+
if (Number.isInteger(event.data.turn)) body += format(t.turnSuffix, { turn: event.data.turn });
|
|
359
480
|
if (cfg.excerpt && window.lastAssistantText) {
|
|
360
481
|
body += "\n" + truncate(window.lastAssistantText, cfg.excerptMaxChars);
|
|
361
482
|
}
|
|
@@ -380,10 +501,11 @@ export const apply = (ctx, config = {}) => {
|
|
|
380
501
|
const data = event.data ?? {};
|
|
381
502
|
const body = typeof data.toolName === "string"
|
|
382
503
|
? (typeof data.reason === "string" && data.reason.length > 0
|
|
383
|
-
?
|
|
384
|
-
:
|
|
385
|
-
:
|
|
386
|
-
|
|
504
|
+
? format(t.approvalToolWithReason, { tool: data.toolName, reason: truncate(data.reason, 100) })
|
|
505
|
+
: format(t.approvalTool, { tool: data.toolName }))
|
|
506
|
+
: t.approvalGeneric;
|
|
507
|
+
const title = cfg.appName + t.approvalTitle;
|
|
508
|
+
sendToast(title, body, { sessionId: session?.id });
|
|
387
509
|
const url = cfg.openOnClick && session?.id ? conversationUrl(session?.id) : undefined;
|
|
388
510
|
log({
|
|
389
511
|
event: "approval",
|
|
@@ -391,6 +513,8 @@ export const apply = (ctx, config = {}) => {
|
|
|
391
513
|
id: data.id,
|
|
392
514
|
toolName: data.toolName,
|
|
393
515
|
reason: data.reason,
|
|
516
|
+
title,
|
|
517
|
+
body,
|
|
394
518
|
...(url ? { url } : {}),
|
|
395
519
|
});
|
|
396
520
|
return;
|
|
@@ -405,12 +529,16 @@ export const apply = (ctx, config = {}) => {
|
|
|
405
529
|
question = extractQuestionFromCode(data.arguments);
|
|
406
530
|
}
|
|
407
531
|
if (question !== undefined || data.name === "ask_user_question") {
|
|
408
|
-
|
|
532
|
+
const body = question ? truncate(question, 100) : t.askFallback;
|
|
533
|
+
const title = cfg.appName + t.askTitle;
|
|
534
|
+
sendToast(title, body, { sessionId: session?.id });
|
|
409
535
|
const url = cfg.openOnClick && session?.id ? conversationUrl(session?.id) : undefined;
|
|
410
536
|
log({
|
|
411
537
|
event: "ask-user",
|
|
412
538
|
sessionId: session?.id,
|
|
413
539
|
question: question ?? null,
|
|
540
|
+
title,
|
|
541
|
+
body,
|
|
414
542
|
...(url ? { url } : {}),
|
|
415
543
|
});
|
|
416
544
|
}
|
|
@@ -433,10 +561,10 @@ export const apply = (ctx, config = {}) => {
|
|
|
433
561
|
sessionId: null,
|
|
434
562
|
});
|
|
435
563
|
if (cfg.enabled && !channelOk) {
|
|
436
|
-
ctx.logger?.warn?.("dsh-notify:
|
|
564
|
+
ctx.logger?.warn?.("dsh-notify: " + t.warnChannelUnavailable);
|
|
437
565
|
}
|
|
438
566
|
if (cfg.enabled && cfg.notifyOnStart) {
|
|
439
567
|
// Activation toast has no session: leave non-clickable (legacy behavior).
|
|
440
|
-
sendToast(cfg.appName,
|
|
568
|
+
sendToast(cfg.appName, format(t.activated, { version: VERSION }));
|
|
441
569
|
}
|
|
442
570
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-notify-windows",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "DeepSeek Harness (DSH)
|
|
3
|
+
"version": "0.8.0-beta.0",
|
|
4
|
+
"description": "DeepSeek Harness (DSH) \u63d2\u4ef6\uff1a\u4efb\u52a1\u5b8c\u6210 / \u7b49\u5f85\u5ba1\u6279 / \u7b49\u5f85\u56de\u7b54\u65f6\u53d1\u9001 Windows \u7cfb\u7edf\u684c\u9762\u901a\u77e5",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/SeverusZh/dsh-notify-windows.git"
|