dsh-plugin-notify-sounds 1.6.2 → 1.6.3
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.md +1 -0
- package/README.zh.md +1 -0
- package/lib/client.js +13 -4
- package/lib/index.js +14 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -123,6 +123,7 @@ For anyone hacking on the code (exact details live in the source comments):
|
|
|
123
123
|
|
|
124
124
|
## Changelog
|
|
125
125
|
|
|
126
|
+
- **v1.6.3** (2026-10-11) The update-success message no longer flashes and vanishes: the Host remembers the install and repeats it with every check, so "Updated, restart DSH to load it" survives the page reload the update itself triggers (it swaps the client half's own file) and stays until the restart. The cached check is invalidated after an install too, so the row can no longer claim a newer version than the one it just wrote.
|
|
126
127
|
- **v1.6.2** (2026-10-11) Fixed one-click update always failing when the mainland mirror answered the check: the mirror serves its tarballs from its CDN, and the downloader refused every redirect — so the download could never finish (found by the live drill). Redirects are now followed one hop at a time with every hop checked against the trusted hosts (the mirror's CDN is the one legitimate hop); the sha512 check is unchanged. A failure now names the stage it died at (check / download / verify / unpack / replace).
|
|
127
128
|
- **v1.6.1** (2026-10-11) The README is rebuilt as a release page: a new **Consistent with DSH's own UI** section on how the UI matches DSH's own settings, copy polished throughout, and the upgrade notes corrected — the plugin updates itself, so no uninstall is needed. This release doubles as the live drill for one-click update.
|
|
128
129
|
- **v1.6.0** (2026-10-11) New: **check for updates and install them in one click**. The settings section can fetch the newest npm version and install it: the download is checked against the registry's published sha512, only whitelisted files are unpacked (symlinks and path traversal are refused), the old files are kept aside, and any failure restores them. Downloaded code is never executed — the new code loads when DSH restarts. Automatic checks run at most once a day and can be turned off, with the mainland-China mirror as fallback.
|
package/README.zh.md
CHANGED
|
@@ -123,6 +123,7 @@ dsh plugin --profile <你的 profile> add ./dsh-plugin-notify-sounds
|
|
|
123
123
|
|
|
124
124
|
## 更新记录
|
|
125
125
|
|
|
126
|
+
- **v1.6.3**(2026-10-11)更新成功的提示不再一闪而过:更新结果改由**宿主**记住、随每次检查下发——即便更新替换掉了客户端自身文件、页面热重载丢掉本地状态,「更新完成:vX → vY,重启 DSH 后生效」也会一直显示到你重启;同时更新后**作废检查缓存**,不会再出现「明明刚升完却仍显示有新版」。
|
|
126
127
|
- **v1.6.2**(2026-10-11)修复一键更新在**镜像源**下必然失败:npmmirror 的 tarball 会跳转到它的 CDN,而下载器此前拒绝一切重定向,于是「只要检查由镜像应答,下载就永远到不了」(真机演练实测发现)。现在重定向改为逐跳跟随、每一跳都必须仍落在受信主机上(镜像 CDN 是唯一合法跳转),sha512 校验不变;失败提示会带上失败阶段(下载 / 校验 / 解包 / 替换),便于定位。
|
|
127
128
|
- **v1.6.1**(2026-10-11)README 重排为发布页风格:补「**与 DSH 界面保持一致**」一节讲清界面取自 DSH 自身设计体系;通篇表达润色,并修正升级说明(本插件自带更新,无需卸载重装)。本版同时是「一键更新」的发布演练。
|
|
128
129
|
- **v1.6.0**(2026-10-11)新增**检查更新与一键更新**:设置里可查 npm 新版本并一键安装——下载后按 registry 公布的 sha512 校验、只解包白名单文件(拒绝符号链接与路径穿越)、先备份再替换、失败自动还原;下载内容从不执行,新代码在 DSH 重启时生效。自动检查每天最多一次、可关;npm 官方源不通时自动改用国内镜像。
|
package/lib/client.js
CHANGED
|
@@ -1583,6 +1583,14 @@ window.__ModuleLoader__.load({
|
|
|
1583
1583
|
}, []);
|
|
1584
1584
|
const payload = state.payload;
|
|
1585
1585
|
const version = (text) => (typeof text === "string" && text !== "" ? `v${text}` : "?");
|
|
1586
|
+
// The Host remembers the last install and repeats it with every
|
|
1587
|
+
// answer, and this row prefers that: an update swaps the client
|
|
1588
|
+
// half's own file, so the page may reload and drop this row's
|
|
1589
|
+
// state mid-message — the news has to survive that. The Host keeps
|
|
1590
|
+
// it until DSH restarts, which is exactly as long as "restart to
|
|
1591
|
+
// load it" is true.
|
|
1592
|
+
const applied = payload?.applied ?? (state.phase === "updated" ? state.result : undefined);
|
|
1593
|
+
const appliedDone = applied !== undefined && (applied.ok === true || typeof applied.to === "string");
|
|
1586
1594
|
let status = payload === undefined
|
|
1587
1595
|
? t("update.idle")
|
|
1588
1596
|
: payload.updateAvailable === true
|
|
@@ -1590,10 +1598,11 @@ window.__ModuleLoader__.load({
|
|
|
1590
1598
|
: t("update.uptodate");
|
|
1591
1599
|
if (state.phase === "checking") status = t("update.checking");
|
|
1592
1600
|
if (state.phase === "updating") status = t("update.updating");
|
|
1593
|
-
if (state.phase === "applyFailed") status = t("update.applyFailed", { stage: String(state.result?.stage ?? "?") });
|
|
1594
1601
|
let detail = payload === undefined ? status : `${t("update.current")} ${version(payload.current)} · ${status}`;
|
|
1595
|
-
if (
|
|
1596
|
-
detail = `${t("update.done")}:${version(
|
|
1602
|
+
if (appliedDone) {
|
|
1603
|
+
detail = `${t("update.done")}:${version(applied.from)} → ${version(applied.to)},${t("update.restart")}`;
|
|
1604
|
+
} else if (state.phase === "applyFailed") {
|
|
1605
|
+
detail = t("update.applyFailed", { stage: String(state.result?.stage ?? "?") });
|
|
1597
1606
|
}
|
|
1598
1607
|
const busy = state.phase === "checking" || state.phase === "updating";
|
|
1599
1608
|
return h("div", { className: css.row }, [
|
|
@@ -1603,7 +1612,7 @@ window.__ModuleLoader__.load({
|
|
|
1603
1612
|
h("div", { className: css.hint, key: "status" }, detail),
|
|
1604
1613
|
]),
|
|
1605
1614
|
h("div", { className: css.stackControl, key: "control" }, [
|
|
1606
|
-
payload?.updateAvailable === true &&
|
|
1615
|
+
payload?.updateAvailable === true && !appliedDone ? h(Button, {
|
|
1607
1616
|
key: "apply",
|
|
1608
1617
|
variant: "outline",
|
|
1609
1618
|
size: "sm",
|
package/lib/index.js
CHANGED
|
@@ -468,8 +468,8 @@ const UPDATE_CHECK_SPACING_MS = 15 * 1000;
|
|
|
468
468
|
const UPDATE_FETCH_TIMEOUT_MS = 10 * 1000;
|
|
469
469
|
const UPDATE_MAX_RESPONSE_BYTES = 256 * 1024;
|
|
470
470
|
|
|
471
|
-
/** Result of the last check,
|
|
472
|
-
const updateCheck = { checkedAt: 0, requestedAt: 0, result: undefined };
|
|
471
|
+
/** Result of the last check, when the last real request went out, and the last successful install. */
|
|
472
|
+
const updateCheck = { checkedAt: 0, requestedAt: 0, result: undefined, applied: undefined };
|
|
473
473
|
|
|
474
474
|
/**
|
|
475
475
|
* Parse `major.minor.patch` with an optional prerelease tag.
|
|
@@ -603,7 +603,7 @@ export async function checkForUpdate(options = {}) {
|
|
|
603
603
|
const current = readOwnVersion(options.root);
|
|
604
604
|
const answer = await fetchRegistryAnswer();
|
|
605
605
|
if (answer === undefined) {
|
|
606
|
-
const result = { ok: false, current, error: "no registry answered" };
|
|
606
|
+
const result = { ok: false, current, error: "no registry answered", applied: updateCheck.applied };
|
|
607
607
|
updateCheck.checkedAt = now;
|
|
608
608
|
updateCheck.result = result;
|
|
609
609
|
return result;
|
|
@@ -615,6 +615,7 @@ export async function checkForUpdate(options = {}) {
|
|
|
615
615
|
latest: answer.version,
|
|
616
616
|
updateAvailable: Number.isFinite(ordering) && ordering < 0,
|
|
617
617
|
checkedAt: now,
|
|
618
|
+
applied: updateCheck.applied,
|
|
618
619
|
};
|
|
619
620
|
updateCheck.checkedAt = now;
|
|
620
621
|
updateCheck.result = result;
|
|
@@ -882,6 +883,16 @@ export async function applyUpdate(options = {}) {
|
|
|
882
883
|
const files = unpackTar(gunzipSync(bytes));
|
|
883
884
|
stage = "replace";
|
|
884
885
|
installUpdate(root, files);
|
|
886
|
+
// The answer that said "update available" is now a lie about this
|
|
887
|
+
// installation: drop the cached check so the next one re-reads the
|
|
888
|
+
// manifest this install just wrote. The outcome is remembered too —
|
|
889
|
+
// the very files that were just swapped include the client half, so
|
|
890
|
+
// the page may reload and lose its own state, and the news that an
|
|
891
|
+
// update landed must survive that. It is kept until DSH restarts,
|
|
892
|
+
// which is exactly as long as "restart to load it" is true.
|
|
893
|
+
updateCheck.checkedAt = 0;
|
|
894
|
+
updateCheck.result = undefined;
|
|
895
|
+
updateCheck.applied = { from: current, to: answer.version, at: Date.now() };
|
|
885
896
|
log(`update applied: ${current ?? "?"} -> ${answer.version}`);
|
|
886
897
|
return { ok: true, from: current, to: answer.version, requiresRestart: true };
|
|
887
898
|
} catch (error) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-plugin-notify-sounds",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.3",
|
|
4
4
|
"description": "Notification sounds for DeepSeek Harness — a soft chime when the agent needs your decision or finishes a task, with a Settings section styled like DSH's own · 需要决策或任务完成时播放提示音,点击系统通知可回到窗口,设置面板可调音量与音效",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|