dsh-selfupdater 0.4.11 → 0.4.13
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/client/settings-card.js +16 -6
- package/lib/plugin-update-task.mjs +50 -2
- package/package.json +1 -1
package/client/settings-card.js
CHANGED
|
@@ -70,13 +70,18 @@ async function api(path, options) {
|
|
|
70
70
|
/** 各小节消息的"首次出现"记录(dsh / plugin 两个通道独立计时)。 */
|
|
71
71
|
const msgSeen = { dsh: { text: '', at: 0 }, plugin: { text: '', at: 0 } };
|
|
72
72
|
|
|
73
|
+
/** 需要持续显示消息的终态(完成/失败类):"请重启生效/失败原因"不能被 TTL 藏掉。 */
|
|
74
|
+
const FINAL_MSG_STATES = ['done', 'done_failed', 'done_pending_restart', 'error'];
|
|
75
|
+
|
|
73
76
|
/**
|
|
74
77
|
* 按时效过滤消息:新文本重置计时并显示;相同文本未超时继续显示;
|
|
75
78
|
* 超过 MSG_TTL_MS 返回空串(隐藏)。切换菜单回来时轮询仍在跑,
|
|
76
|
-
*
|
|
79
|
+
* 过期消息自然不再渲染。终态(完成/失败)消息豁免 TTL,持续显示
|
|
80
|
+
* 直到状态变化 —— 用户必须清楚看到"更新成功需重启"或失败原因。
|
|
77
81
|
*/
|
|
78
|
-
function agedMessage(section, text) {
|
|
82
|
+
function agedMessage(section, text, isFinal) {
|
|
79
83
|
if (typeof text !== 'string' || text === '') return '';
|
|
84
|
+
if (isFinal) return text; // 终态不参与计时
|
|
80
85
|
const seen = msgSeen[section];
|
|
81
86
|
if (text !== seen.text) {
|
|
82
87
|
seen.text = text;
|
|
@@ -638,8 +643,11 @@ function apply(ctx) {
|
|
|
638
643
|
async function pollStatus() {
|
|
639
644
|
try {
|
|
640
645
|
const data = await api('/status');
|
|
641
|
-
//
|
|
642
|
-
latestStatus = {
|
|
646
|
+
// 消息按时效过滤:普通提示过期隐藏;终态(完成/失败)持续显示。
|
|
647
|
+
latestStatus = {
|
|
648
|
+
...data,
|
|
649
|
+
message: agedMessage('dsh', data.message, FINAL_MSG_STATES.includes(data.state)),
|
|
650
|
+
};
|
|
643
651
|
upgradeStarting = false; // 服务端状态已接管视觉
|
|
644
652
|
} catch { /* 服务重启期间拉不到状态属正常:保持 starting 视觉 */ }
|
|
645
653
|
refresh();
|
|
@@ -668,8 +676,10 @@ function apply(ctx) {
|
|
|
668
676
|
pluginData = data; // 响应本身就是单对象:{ currentVersion, latestVersion, ... }
|
|
669
677
|
pluginBusy = data.busy === true || PLUGIN_BUSY_STATES.includes(data.state);
|
|
670
678
|
if (!pluginBusy) {
|
|
671
|
-
//
|
|
672
|
-
|
|
679
|
+
// 空闲时才显示消息:普通提示(发现新版本/已是最新)按时效隐藏;
|
|
680
|
+
// 终态(已装待重启/失败原因)豁免 TTL 持续显示,确保用户看到结果。
|
|
681
|
+
const isFinal = FINAL_MSG_STATES.includes(data.state);
|
|
682
|
+
pluginMsg = agedMessage('plugin', data.message, isFinal);
|
|
673
683
|
}
|
|
674
684
|
// 服务端 busy 或终态已可见:乐观标志功成身退。
|
|
675
685
|
if (pluginBusy || data.state === 'done_pending_restart' || data.state === 'error') {
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
import { spawn } from 'node:child_process';
|
|
19
19
|
import { createRequire } from 'node:module';
|
|
20
|
-
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
20
|
+
import { copyFileSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
21
21
|
import { dirname, join, resolve } from 'node:path';
|
|
22
22
|
import { fileURLToPath } from 'node:url';
|
|
23
23
|
|
|
@@ -193,6 +193,44 @@ export function installedSelfVersion(workspace) {
|
|
|
193
193
|
return resolveInstalledVersion(profileDir);
|
|
194
194
|
}
|
|
195
195
|
|
|
196
|
+
/**
|
|
197
|
+
* 同步 FPK 内置插件种子目录(APP_DIR/plugins/),防止重启回退。0.4.12 新增。
|
|
198
|
+
*
|
|
199
|
+
* 【为什么要同步种子】runner.js 每次启动都会执行 installBundledPlugins():
|
|
200
|
+
* 读 bundled.txt 清单,发现"种子 tgz 版本 ≠ profile 已装版本"就用种子重装。
|
|
201
|
+
* 在线更新装上新版后(如 0.4.11),种子还是 FPK 打包时的旧版(如 0.4.10),
|
|
202
|
+
* 重启即被覆盖回旧版 —— 这正是"更新成功但重启后版本回退"的根因。
|
|
203
|
+
*
|
|
204
|
+
* 同步内容:新版 tgz 写入种子目录 + bundled.txt 清单行替换 + 删除旧版 tgz。
|
|
205
|
+
* 同步后 runner 下次启动看到"已装版本 == 种子版本"即跳过重装,更新得以存活。
|
|
206
|
+
*
|
|
207
|
+
* @returns {string|null} 失败原因;成功返回 null。
|
|
208
|
+
*/
|
|
209
|
+
function syncBundledSeed(appDir, stagingTgz, newVersion) {
|
|
210
|
+
try {
|
|
211
|
+
const seedDir = join(appDir, 'plugins');
|
|
212
|
+
const stagedName = `${SELF_NAME}-${newVersion}.tgz`;
|
|
213
|
+
const listFile = join(seedDir, 'bundled.txt');
|
|
214
|
+
// 无种子目录说明不是 FPK 部署(本地开发等),无需同步。
|
|
215
|
+
if (!existsSync(listFile)) return null;
|
|
216
|
+
// 1. 新版 tgz 就位。
|
|
217
|
+
copyFileSync(stagingTgz, join(seedDir, stagedName));
|
|
218
|
+
// 2. bundled.txt 里本插件的行替换为新文件名(其他插件行不动)。
|
|
219
|
+
const lines = readFileSync(listFile, 'utf8').split('\n')
|
|
220
|
+
.map((line) => /^dsh-selfupdater-[\w.-]+\.tgz$/.test(line.trim()) ? stagedName : line);
|
|
221
|
+
writeFileSync(listFile, lines.join('\n'));
|
|
222
|
+
// 3. 清掉旧版种子 tgz(文件名带版本号,不删则堆积)。
|
|
223
|
+
for (const f of readdirSync(seedDir)) {
|
|
224
|
+
if (f !== stagedName && /^dsh-selfupdater-[\w.-]+\.tgz$/.test(f)) {
|
|
225
|
+
rmSync(join(seedDir, f), { force: true });
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return null;
|
|
229
|
+
} catch (err) {
|
|
230
|
+
return err.message;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
196
234
|
/**
|
|
197
235
|
* 定位 dsh CLI 入口(原地安装新版的执行器)。
|
|
198
236
|
* 0.4.10 修复:此前硬编码 bin/dsh.js,但 dsh 包实际 bin 是 lib/bin.js
|
|
@@ -382,9 +420,19 @@ export async function runPluginUpdateTask({ appDir, workspace, logger }) {
|
|
|
382
420
|
warnLog(`清单声明修正失败(不影响已安装文件): ${err.message}`);
|
|
383
421
|
}
|
|
384
422
|
|
|
423
|
+
/* 6.8 同步 FPK 内置种子(须在 finally 删除暂存 tgz 前复制):
|
|
424
|
+
runner.js 每次启动会用种子重装"版本不一致"的插件,不同步则
|
|
425
|
+
重启即回退到 FPK 打包时的旧版;失败时在完成消息中如实告知。 */
|
|
426
|
+
const seedErr = syncBundledSeed(appDir, stagingTgz, release.version);
|
|
427
|
+
if (seedErr !== null) {
|
|
428
|
+
warnLog(`内置种子同步失败: ${seedErr}`);
|
|
429
|
+
}
|
|
430
|
+
|
|
385
431
|
/* 7. 成功:不重启宿主,提示用户重启使新版本生效。 */
|
|
386
432
|
setState('done_pending_restart',
|
|
387
|
-
|
|
433
|
+
seedErr === null
|
|
434
|
+
? `插件已更新到 ${release.version},请重启 DeepSeek Harness 使新版本生效`
|
|
435
|
+
: `插件已更新到 ${release.version},但内置种子同步失败(${seedErr}),重启后可能回退到旧版,建议重新安装 FPK`,
|
|
388
436
|
{ finishedAt: new Date().toISOString(), targetVersion: release.version });
|
|
389
437
|
log(`插件更新完成(${installed} → ${release.version}),等待用户重启生效`);
|
|
390
438
|
} catch (err) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-selfupdater",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.13",
|
|
4
4
|
"description": "Self-update plugin for DeepSeek Harness: DSH core upgrades via detached swap script; plugin self-update installs in-place without killing the host and prompts for restart. DSH 主程序与已装插件的一站式在线更新插件。",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|