dsh-life-pack 0.3.0 → 0.3.2
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/dist/client.js +199 -63
- package/dist/client.js.map +1 -1
- package/dist/health-fetch.d.ts +9 -2
- package/dist/health-fetch.d.ts.map +1 -1
- package/dist/health-fetch.js +9 -2
- package/dist/health-fetch.js.map +1 -1
- package/dist/update-client.d.ts +7 -1
- package/dist/update-client.d.ts.map +1 -1
- package/dist/update-client.js +7 -1
- package/dist/update-client.js.map +1 -1
- package/dist/update-contract.d.ts.map +1 -1
- package/dist/update-contract.js +29 -18
- package/dist/update-contract.js.map +1 -1
- package/dist/update-env.d.ts.map +1 -1
- package/dist/update-env.js +7 -11
- package/dist/update-env.js.map +1 -1
- package/dist/update-view.d.ts +41 -6
- package/dist/update-view.d.ts.map +1 -1
- package/dist/update-view.js +88 -21
- package/dist/update-view.js.map +1 -1
- package/package.json +1 -1
package/dist/client.js
CHANGED
|
@@ -186,28 +186,37 @@ window.__ModuleLoader__.load({
|
|
|
186
186
|
* 一处定义、两侧引用:宿主半 `manager-version.ts` 读失败时回它,面板半 `update-client.ts`
|
|
187
187
|
* 归一化时也认它——面板侧不许 import 宿主半(会把 node 内建带进浏览器束)。 */
|
|
188
188
|
const VERSION_UNKNOWN = "unknown";
|
|
189
|
-
/** 装不了 / 装不成的人话原因表:八种 `blockedReason` + 过程错误码(更新包 README 第 8、12 节)。
|
|
189
|
+
/** 装不了 / 装不成的人话原因表:八种 `blockedReason` + 过程错误码(更新包 README 第 8、12 节)。
|
|
190
|
+
*
|
|
191
|
+
* 每条都要三段齐全(票 #740 的文案判据):**事实**(哪里对不上)→ **后果**(这家现在能不能装)
|
|
192
|
+
* → **动作**(用户做得到、且面板上真有的那一步;面板代劳不了的走「下面这条命令」)。
|
|
193
|
+
* 禁用口语与自造词:「半截任务」「电话没接上」「回执异常」「还出现就重装」「查宿主日志」一律不写。
|
|
194
|
+
*
|
|
195
|
+
* 点名的按钮必须是**这一行上真够得着**的那颗(票 #740 第三轮):结果浮层一开就盖住题头的
|
|
196
|
+
* 「检查更新」,所以「改完再读一次」这类下一步一律写「重新检查」(画在那一行上);
|
|
197
|
+
* 只有发生在**重启之后**的下一步才写「检查更新」。反过来,凡是写着「下面这条命令」的那几句,
|
|
198
|
+
* 那一行的命令块必须真出来(判据 `Verdict.manualHint`)——两处都由门禁守着。 */
|
|
190
199
|
const REASON_TEXT = {
|
|
191
|
-
"unknown-profile": "
|
|
192
|
-
"source-install": "
|
|
193
|
-
"invalid-installation": "
|
|
194
|
-
"installation-changed": "
|
|
195
|
-
"pending-restart": "
|
|
196
|
-
"registry-conflict": "
|
|
197
|
-
"incompatible-node": "
|
|
198
|
-
"recovery-required": "
|
|
199
|
-
"check-failed": "
|
|
200
|
-
"invalid-release": "
|
|
201
|
-
"check-expired": "
|
|
202
|
-
"update-busy": "
|
|
203
|
-
"install-failed": "
|
|
204
|
-
"manager-unreachable": "
|
|
205
|
-
"bad-request": "
|
|
206
|
-
internal: "
|
|
200
|
+
"unknown-profile": "认不出这台机器上的安装位置(使用范围的目录不存在,或名字不合法)。把使用范围目录建好,再点「重新检查」。",
|
|
201
|
+
"source-install": "这一份不是从 npm 发行包装的(源码装),面板不能更新它。要更新,照下面这条命令重装,装完再点「重新检查」。",
|
|
202
|
+
"invalid-installation": "磁盘上这一份装得不完整(包名或版本对不上、入口文件缺失)。照下面这条命令重装,装完再点「重新检查」。",
|
|
203
|
+
"installation-changed": "这台机器的安装清单变了(在别处装过、或清单被手工改过),这一家现在不能装。点「重新检查」重新读一次,再点「装上更新」。",
|
|
204
|
+
"pending-restart": "新版已装到磁盘,正在运行的是旧版本。重启 DSH(退出后重新打开)后生效。",
|
|
205
|
+
"registry-conflict": "使用范围清单里写的版本与磁盘上的实际版本对不上。打开使用范围的 `package.json`,把这一行改成磁盘上的版本号,再点「重新检查」。",
|
|
206
|
+
"incompatible-node": "新版本要求 Node 22 或更高,当前运行的是更低版本。升级 Node、重启 DSH 后,再点「检查更新」。",
|
|
207
|
+
"recovery-required": "上次安装没有收尾。点「重试安装」再跑一次;还出现这一条,就用下面这条命令重装。",
|
|
208
|
+
"check-failed": "查不到最新版本:联网失败,或官方源(registry.npmjs.org)不可达。确认网络与源地址后,再点「重新检查」。",
|
|
209
|
+
"invalid-release": "官方源上这个版本的元数据不完整(包名或版本不合法、包地址或校验值缺失)。换一个版本,或稍后重试。",
|
|
210
|
+
"check-expired": "上次检查的结果已过期。点「重新检查」重查一次,再点「装上更新」。",
|
|
211
|
+
"update-busy": "同一时间只能安装一个插件。等这次装完,再点「装上更新」。",
|
|
212
|
+
"install-failed": "安装没有完成。在终端里执行下面这条命令把它装完,装完再点「重新检查」。",
|
|
213
|
+
"manager-unreachable": "面板连不上总管插件(宿主里没注册,或版本对不上)。重启 DSH 后重试;仍不行就在 DSH 日志里找 `[dsh-life-pack]` 的报错。",
|
|
214
|
+
"bad-request": "面板与总管之间的请求出错了(属程序缺陷)。请在 DSH 日志里找 `[dsh-life-pack]` 的报错,连同这家插件的名字报到本仓 issue。",
|
|
215
|
+
internal: "面板与总管之间的请求出错了(属程序缺陷)。请在 DSH 日志里找 `[dsh-life-pack]` 的报错,连同这家插件的名字报到本仓 issue。"
|
|
207
216
|
};
|
|
208
217
|
/** 原因码 → 人话(认不出的码不猜、原样回码并说明这是未知原因)。 */
|
|
209
218
|
function reasonText(code) {
|
|
210
|
-
return REASON_TEXT[code] ?? "
|
|
219
|
+
return REASON_TEXT[code] ?? "面板不认识这个原因码(" + code + ")。请在 DSH 日志里找同一处报错,报到本仓 issue。";
|
|
211
220
|
}
|
|
212
221
|
//#endregion
|
|
213
222
|
//#region src/update-client.ts
|
|
@@ -301,7 +310,13 @@ window.__ModuleLoader__.load({
|
|
|
301
310
|
version
|
|
302
311
|
});
|
|
303
312
|
}
|
|
304
|
-
/** 装上更新:查新版 → 提交安装 → 有界轮询查状态到任务收尾;凭证过期重查一次再提交。
|
|
313
|
+
/** 装上更新:查新版 → 提交安装 → 有界轮询查状态到任务收尾;凭证过期重查一次再提交。
|
|
314
|
+
*
|
|
315
|
+
* **第一句那次 `checkTarget` 不是多余的**(票 #740 第四轮):凭证是「这一刻的安装态」的凭据,
|
|
316
|
+
* 更新包提交时拿它跟当时读到的安装态比(`service.js:309`/`:325`),不一样就拒。
|
|
317
|
+
* 而面板**从来只提交这一刻现签的凭证**——这就是「刚在本面板装过别家,这一家照样点得动
|
|
318
|
+
* 「装上更新」」的全部依据:旧快照只用来显示版本,不参与提交。(第四轮以前面板还把别家整行
|
|
319
|
+
* 换成「重新检查」——那是白挡一次本来会成功的点击,已删。) */
|
|
305
320
|
async function updateInstalled(call, target, pollMs) {
|
|
306
321
|
if (!target.phones) return failure("bad-request", { target: target.key });
|
|
307
322
|
const first = await checkTarget(call, target);
|
|
@@ -357,57 +372,125 @@ window.__ModuleLoader__.load({
|
|
|
357
372
|
if (target.panelRegistered === false) return "unregistered-product";
|
|
358
373
|
return "not-connected";
|
|
359
374
|
}
|
|
360
|
-
/**
|
|
375
|
+
/** 这一家是不是「装好了、只差重启」——判据是**事实**(磁盘版本 ≠ 正在跑的版本),不是原因码。
|
|
376
|
+
*
|
|
377
|
+
* 为什么不许按原因码判(票 #740 真机):同一种事实在更新核心里可能报成两种码——
|
|
378
|
+
* `pending-restart`,或者被上一次中断的任务盖成 `recovery-required`。按码判会把「该重启」
|
|
379
|
+
* 漏成一句「装不上」,用户看到的正是那种读不懂的屏。 */
|
|
380
|
+
function restartPendingOf(snapshot) {
|
|
381
|
+
const installed = snapshot.installedVersion;
|
|
382
|
+
return installed !== null && installed !== snapshot.runningVersion;
|
|
383
|
+
}
|
|
384
|
+
/** 待重启那一句(三段齐全:事实 → 后果 → 动作)。
|
|
385
|
+
*
|
|
386
|
+
* 措辞不写「新版」:磁盘上那份也可能比正在跑的**低**(手工降级),「新版」就不实了;
|
|
387
|
+
* 只照事实说「磁盘上装的是 X、正在运行的是 Y」。
|
|
388
|
+
* 动作只给「重启 DSH」:这一态下面板**做不了别的**——更新包的可装判据要求最新版严格高于
|
|
389
|
+
* **正在运行**的版本(`service.js:219`),而凭证只发在可装态(`service.js:256`:`receipt` 只在
|
|
390
|
+
* `canInstall` 时给),所以这一态里「重试安装」点了也是白点。 */
|
|
391
|
+
function restartLine(snapshot) {
|
|
392
|
+
const installed = snapshot.installedVersion ?? snapshot.job?.targetVersion ?? "新版本";
|
|
393
|
+
return "磁盘上装的是 " + String(installed) + ";正在运行的是 " + snapshot.runningVersion + "。重启 DSH(退出后重新打开)后生效。";
|
|
394
|
+
}
|
|
395
|
+
/** 「条件在别处(目录/清单/别处装的),改完回来重读一次」的原因码:面板能给的下一步就是「重新检查」。
|
|
396
|
+
*
|
|
397
|
+
* 为什么要逐码列(票 #740 第三轮的朗读表):`unknown-profile`/`registry-conflict`/`invalid-installation`/
|
|
398
|
+
* `source-install` 这四态原先一个动作都不给,屏上那句却写着「再点「检查更新」」——那颗按钮住在面板题头,
|
|
399
|
+
* 结果浮层一开(点「检查更新」就开)就被浮层盖住,够不着。跟两张「还没查过」的卡是同一类病。 */
|
|
400
|
+
const RECHECK_REASONS = /* @__PURE__ */ new Set([
|
|
401
|
+
"installation-changed",
|
|
402
|
+
"unknown-profile",
|
|
403
|
+
"registry-conflict",
|
|
404
|
+
"invalid-installation",
|
|
405
|
+
"source-install"
|
|
406
|
+
]);
|
|
407
|
+
/** 「这一步面板代劳不了、得照下面那条命令做」的原因码:这些话下面必须真有命令块。 */
|
|
408
|
+
const NEEDS_MANUAL = /* @__PURE__ */ new Set([
|
|
409
|
+
"source-install",
|
|
410
|
+
"invalid-installation",
|
|
411
|
+
"recovery-required"
|
|
412
|
+
]);
|
|
413
|
+
/** 拦截态各自给得出的下一步:面板能代劳的给按钮,代劳不了的给 null(那种走手工命令块)。 */
|
|
414
|
+
function actionForBlocked(reason) {
|
|
415
|
+
if (RECHECK_REASONS.has(reason)) return "recheck";
|
|
416
|
+
if (reason === "recovery-required") return "retry";
|
|
417
|
+
return null;
|
|
418
|
+
}
|
|
419
|
+
/** 这一行此刻该画哪颗按钮:结论自带的动作优先,**失败的行一律给「重新检查」**(票 #740 第三轮)。
|
|
420
|
+
*
|
|
421
|
+
* 失败的行(查不到最新版/检查过期/装失败)没有快照、也就没有结论,原先一颗按钮都不画:
|
|
422
|
+
* 屏上写着「再点「检查更新」」,那颗按钮却在浮层底下 ⇒ 无路可走。重新查一次对每一种失败都成立。 */
|
|
423
|
+
function cardActionOf(verdict, phase) {
|
|
424
|
+
if (verdict?.action) return verdict.action;
|
|
425
|
+
return phase === "failed" ? "recheck" : null;
|
|
426
|
+
}
|
|
427
|
+
/** 这一行要不要摊出那条手工命令(票 #740 第三轮定的形状,第四轮补的这一格)。
|
|
428
|
+
*
|
|
429
|
+
* 三种情形摊:① 上一次装失败(`failed`);② 这句话自己指着「下面这条命令」(`manualHint`)。
|
|
430
|
+
* **待重启**与 `incompatible-node` 不摊:它们那时该做的是**重启**(或升级 Node),不是重装一次。
|
|
431
|
+
* (第四轮维护者真机截图逮到:待重启那张卡下面挂着一条「dsh plugin … add」——点它等于把刚装好的
|
|
432
|
+
* 那一版再装一遍,纯噪声,还会让人以为状态不对。) */
|
|
433
|
+
function showManualOf(verdict, phase) {
|
|
434
|
+
return phase === "failed" || verdict?.manualHint === true;
|
|
435
|
+
}
|
|
436
|
+
/** 把快照翻成一行结论:还没查 / 已是最新 / 有新版 / 待重启 / 装不了(附人话原因与下一步)。 */
|
|
361
437
|
function verdictOf(target, snapshot) {
|
|
362
438
|
const absent = isAbsent(target);
|
|
363
439
|
const latest = snapshot.latestVersion;
|
|
364
440
|
if (absent && latest === null) return {
|
|
365
441
|
kind: "unknown",
|
|
366
|
-
text: "
|
|
367
|
-
action:
|
|
442
|
+
text: "未安装。点「重新检查」查最新版本,再点「装上」。",
|
|
443
|
+
action: "recheck"
|
|
368
444
|
};
|
|
369
445
|
if (absent) return {
|
|
370
446
|
kind: "update-available",
|
|
371
|
-
text: "未安装;最新版本 " + String(latest) + "
|
|
447
|
+
text: "未安装;最新版本 " + String(latest) + "。点「装上」安装。",
|
|
372
448
|
action: "install"
|
|
373
449
|
};
|
|
374
|
-
if (snapshot
|
|
450
|
+
if (restartPendingOf(snapshot)) return {
|
|
451
|
+
kind: "blocked",
|
|
452
|
+
text: restartLine(snapshot),
|
|
453
|
+
action: null
|
|
454
|
+
};
|
|
455
|
+
const installed = snapshot.installedVersion;
|
|
456
|
+
const hasNewer = latest !== null && latest !== installed;
|
|
457
|
+
if (snapshot.blockedReason !== null && hasNewer) {
|
|
375
458
|
const blocked = snapshot.blockedReason;
|
|
376
|
-
if (blocked === "pending-restart") return {
|
|
377
|
-
kind: "blocked",
|
|
378
|
-
text: reasonText(blocked),
|
|
379
|
-
action: null
|
|
380
|
-
};
|
|
381
459
|
return {
|
|
382
460
|
kind: "blocked",
|
|
383
461
|
text: reasonText(blocked),
|
|
384
|
-
action:
|
|
462
|
+
action: actionForBlocked(blocked),
|
|
463
|
+
manualHint: NEEDS_MANUAL.has(blocked)
|
|
385
464
|
};
|
|
386
465
|
}
|
|
387
466
|
if (latest === null) return {
|
|
388
467
|
kind: "unknown",
|
|
389
|
-
text: "
|
|
390
|
-
action:
|
|
468
|
+
text: "还没查过最新版本。点「重新检查」。",
|
|
469
|
+
action: "recheck"
|
|
391
470
|
};
|
|
392
471
|
if (snapshot.canInstall) return {
|
|
393
472
|
kind: "update-available",
|
|
394
|
-
text: "
|
|
473
|
+
text: "有新版本 " + String(latest) + "(正在运行 " + snapshot.runningVersion + ")。点「装上更新」。",
|
|
395
474
|
action: "update"
|
|
396
475
|
};
|
|
476
|
+
if (hasNewer) return {
|
|
477
|
+
kind: "update-available",
|
|
478
|
+
text: "有新版本 " + String(latest) + "(正在运行 " + snapshot.runningVersion + ")。点「重新检查」,再点「装上更新」。",
|
|
479
|
+
action: "recheck"
|
|
480
|
+
};
|
|
397
481
|
return {
|
|
398
482
|
kind: "up-to-date",
|
|
399
|
-
text: "
|
|
483
|
+
text: "已是最新版本 " + String(latest) + "。",
|
|
400
484
|
action: null
|
|
401
485
|
};
|
|
402
486
|
}
|
|
403
|
-
/**
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
if (
|
|
409
|
-
|
|
410
|
-
return "⚠️ 新版 " + installed + " 已装好,正在跑的还是 " + running + ",重启宿主后生效。";
|
|
487
|
+
/** 待重启横幅:**一行总账**,只点名哪几家要做「重启 DSH」这件事。
|
|
488
|
+
*
|
|
489
|
+
* 为什么不再逐家重复那句话(票 #740 对抗式审查第三轮的朗读表逮到):同一句「重启 DSH…后生效」
|
|
490
|
+
* 会同时出现在横幅和那家卡片上,一屏两遍。分工定死:**横幅答「谁要重启」,卡片答「为什么、怎么做」**。 */
|
|
491
|
+
function restartBannerText(titles) {
|
|
492
|
+
if (titles.length === 0) return null;
|
|
493
|
+
return "⚠️ 待重启 DSH(退出后重新打开):" + titles.join("、");
|
|
411
494
|
}
|
|
412
495
|
/** 面板展示的那条可复制命令(用户会照着敲的那一条)。
|
|
413
496
|
*
|
|
@@ -692,6 +775,13 @@ window.__ModuleLoader__.load({
|
|
|
692
775
|
default: return "idle";
|
|
693
776
|
}
|
|
694
777
|
}
|
|
778
|
+
/** 四个动作在屏上的名字:按钮文案与判据同源(票 #740)。 */
|
|
779
|
+
const ACTION_LABEL = {
|
|
780
|
+
install: "装上",
|
|
781
|
+
update: "装上更新",
|
|
782
|
+
retry: "重试安装",
|
|
783
|
+
recheck: "重新检查"
|
|
784
|
+
};
|
|
695
785
|
const IDLE$1 = {
|
|
696
786
|
phase: "idle",
|
|
697
787
|
outcome: null,
|
|
@@ -763,8 +853,28 @@ window.__ModuleLoader__.load({
|
|
|
763
853
|
rows,
|
|
764
854
|
targets
|
|
765
855
|
]),
|
|
766
|
-
act: react.useCallback((target) => {
|
|
856
|
+
act: react.useCallback((target, action) => {
|
|
767
857
|
const current = rows[target.key] ?? IDLE$1;
|
|
858
|
+
if (action === "recheck") {
|
|
859
|
+
patch(target.key, {
|
|
860
|
+
phase: "checking",
|
|
861
|
+
outcome: current.outcome,
|
|
862
|
+
failure: null
|
|
863
|
+
});
|
|
864
|
+
(async () => {
|
|
865
|
+
const checked = await checkTarget(getCall(), target);
|
|
866
|
+
patch(target.key, checked.ok ? {
|
|
867
|
+
phase: "ready",
|
|
868
|
+
outcome: checked.value,
|
|
869
|
+
failure: null
|
|
870
|
+
} : {
|
|
871
|
+
phase: "failed",
|
|
872
|
+
outcome: null,
|
|
873
|
+
failure: checked
|
|
874
|
+
});
|
|
875
|
+
})();
|
|
876
|
+
return;
|
|
877
|
+
}
|
|
768
878
|
patch(target.key, {
|
|
769
879
|
...current,
|
|
770
880
|
phase: "installing",
|
|
@@ -836,20 +946,20 @@ window.__ModuleLoader__.load({
|
|
|
836
946
|
} : PANEL_STYLE.btn,
|
|
837
947
|
disabled,
|
|
838
948
|
onClick: checkAll,
|
|
839
|
-
title: "
|
|
949
|
+
title: "检查总管与六家插件的版本"
|
|
840
950
|
}, checking ? "检查中…" : "检查更新");
|
|
841
951
|
}
|
|
842
|
-
/**
|
|
952
|
+
/** 待重启横幅:一行总账,只点名哪几家要重启(理由与做法写在各家卡片上,见 `restartBannerText`)。 */
|
|
843
953
|
function RestartBanners(props) {
|
|
844
|
-
const
|
|
954
|
+
const titles = [];
|
|
845
955
|
for (const target of props.face.targets) {
|
|
846
956
|
const outcome = props.face.rows[target.key]?.outcome;
|
|
847
957
|
if (!outcome) continue;
|
|
848
|
-
|
|
849
|
-
if (text) lines.push(text);
|
|
958
|
+
if (restartPendingOf(outcome.snapshot)) titles.push(target.title);
|
|
850
959
|
}
|
|
851
|
-
|
|
852
|
-
|
|
960
|
+
const text = restartBannerText(titles);
|
|
961
|
+
if (text === null) return null;
|
|
962
|
+
return react.createElement("div", { style: PANEL_STYLE.banner }, text);
|
|
853
963
|
}
|
|
854
964
|
/** 七家结果:装不了的与待重启的常驻(横幅),详细结果放**居中浮层**(点「检查更新」开)。
|
|
855
965
|
*
|
|
@@ -875,7 +985,8 @@ window.__ModuleLoader__.load({
|
|
|
875
985
|
const verdict = snapshot ? verdictOf(target, snapshot) : null;
|
|
876
986
|
const manual = manualForDisplay(target, row.failure?.manual ?? row.outcome?.manual ?? null);
|
|
877
987
|
const busy = row.phase === "installing";
|
|
878
|
-
const showManual = manual !== null && (row.phase
|
|
988
|
+
const showManual = manual !== null && showManualOf(verdict, row.phase);
|
|
989
|
+
const buttonAction = cardActionOf(verdict, row.phase);
|
|
879
990
|
const tone = rowToneOf(row, verdict);
|
|
880
991
|
const paint = ROW_TONE[tone];
|
|
881
992
|
/** 这一行「当前/最新」两个版本号:不等才算有新版,把「最新」那格上色(用户扫一眼先看有没有箭头)。 */
|
|
@@ -906,7 +1017,7 @@ window.__ModuleLoader__.load({
|
|
|
906
1017
|
} : {
|
|
907
1018
|
...PANEL_STYLE.verdictLine,
|
|
908
1019
|
color: "var(--dsw-alias-label-secondary, #9a9a9a)"
|
|
909
|
-
} }, busy ? "正在装,请稍候…" : verdict?.text ?? (checking ? "检查中…" : "")),
|
|
1020
|
+
} }, busy ? "正在装,请稍候…" : verdict?.text ?? (checking ? "检查中…" : "")), buttonAction ? react.createElement("button", {
|
|
910
1021
|
type: "button",
|
|
911
1022
|
style: busy ? {
|
|
912
1023
|
...PANEL_STYLE.btnPrimary,
|
|
@@ -914,11 +1025,13 @@ window.__ModuleLoader__.load({
|
|
|
914
1025
|
cursor: "default"
|
|
915
1026
|
} : PANEL_STYLE.btnPrimary,
|
|
916
1027
|
disabled: busy,
|
|
917
|
-
onClick: () =>
|
|
918
|
-
|
|
1028
|
+
onClick: () => {
|
|
1029
|
+
props.face.act(target, buttonAction);
|
|
1030
|
+
}
|
|
1031
|
+
}, ACTION_LABEL[buttonAction]) : null, row.failure ? react.createElement("div", { style: PANEL_STYLE.reason }, "装不上:" + row.failure.message) : null, versionLines(target, snapshot).map((line, index) => react.createElement("div", {
|
|
919
1032
|
key: String(index),
|
|
920
1033
|
style: PANEL_STYLE.skill
|
|
921
|
-
}, line)), showManual ? react.createElement("code", { style: PANEL_STYLE.cmd }, manual) : null);
|
|
1034
|
+
}, line)), showManual ? react.createElement("div", { style: PANEL_STYLE.skill }, "在终端里执行这条命令可以手动完成这一步:") : null, showManual ? react.createElement("code", { style: PANEL_STYLE.cmd }, manual) : null);
|
|
922
1035
|
});
|
|
923
1036
|
return react.createElement("div", null, react.createElement(RestartBanners, { face: props.face }), open ? react.createElement("div", {
|
|
924
1037
|
style: PANEL_STYLE.overlay,
|
|
@@ -940,7 +1053,7 @@ window.__ModuleLoader__.load({
|
|
|
940
1053
|
onClick: () => close(),
|
|
941
1054
|
title: "关闭",
|
|
942
1055
|
"aria-label": "关闭"
|
|
943
|
-
}, "×")), react.createElement("div", { style: PANEL_STYLE.dialogBody }, body, react.createElement("div", { style: PANEL_STYLE.dialogNote }, "
|
|
1056
|
+
}, "×")), react.createElement("div", { style: PANEL_STYLE.dialogBody }, body, react.createElement("div", { style: PANEL_STYLE.dialogNote }, "安装在宿主后台继续:关掉这块不会中断它。再点「检查更新」看最新进度。")))) : null);
|
|
944
1057
|
}
|
|
945
1058
|
/** 缺席卡:**按两处独立事实分三态**,每态只给能改变状态的动作。
|
|
946
1059
|
*
|
|
@@ -961,17 +1074,19 @@ window.__ModuleLoader__.load({
|
|
|
961
1074
|
const manual = (target ? manualForDisplay(target, row?.failure?.manual ?? row?.outcome?.manual ?? null) : null) ?? props.fallbackCommand;
|
|
962
1075
|
const installed = target ? target.installedVersion ?? target.runningVersion : null;
|
|
963
1076
|
const state = slotStateOf(target, props.inLedger);
|
|
1077
|
+
const blockedVerdict = target && row?.outcome && row.outcome.snapshot.blockedReason !== null ? verdictOf(target, row.outcome.snapshot) : null;
|
|
964
1078
|
let reason = row?.failure?.message ?? null;
|
|
965
|
-
if (
|
|
1079
|
+
if (reason === null && blockedVerdict) reason = blockedVerdict.text;
|
|
1080
|
+
const retryAction = row ? cardActionOf(blockedVerdict, row.phase) : null;
|
|
966
1081
|
let headline;
|
|
967
1082
|
switch (state) {
|
|
968
1083
|
case "absent":
|
|
969
1084
|
headline = target ? "未安装[" + target.packageName + "],装上后这个页签就能用了。" : "这个页签对应的插件还没装。";
|
|
970
1085
|
break;
|
|
971
1086
|
case "unregistered-product":
|
|
972
|
-
headline = target.packageName + " " + String(installed) + "
|
|
1087
|
+
headline = target.packageName + " " + String(installed) + " 已安装,但这一版的插件包里没有「爱生活页签」的注册代码:要等它发布带注册代码的新版本。重启和重装都不会改变这一点。";
|
|
973
1088
|
break;
|
|
974
|
-
default: headline = target.packageName + " " + String(installed) + "
|
|
1089
|
+
default: headline = target.packageName + " " + String(installed) + " 已安装,插件包里也有注册代码,但它的设置页没有加载到面板:重启 DSH 后再看这个页签;仍然这样,就在 DSH 日志里找它的报错。";
|
|
975
1090
|
}
|
|
976
1091
|
return react.createElement("div", { style: PANEL_STYLE.reco }, react.createElement("div", null, headline), state === "absent" ? react.createElement("button", {
|
|
977
1092
|
type: "button",
|
|
@@ -985,9 +1100,23 @@ window.__ModuleLoader__.load({
|
|
|
985
1100
|
},
|
|
986
1101
|
disabled: busy,
|
|
987
1102
|
onClick: () => {
|
|
988
|
-
if (target) props.face.act(target);
|
|
1103
|
+
if (target) props.face.act(target, "install");
|
|
989
1104
|
}
|
|
990
|
-
}, busy ? "正在装,请稍候…" : "装上") : null,
|
|
1105
|
+
}, busy ? "正在装,请稍候…" : "装上") : null, retryAction === "recheck" && target ? react.createElement("button", {
|
|
1106
|
+
type: "button",
|
|
1107
|
+
style: busy ? {
|
|
1108
|
+
...PANEL_STYLE.btn,
|
|
1109
|
+
marginTop: 8,
|
|
1110
|
+
marginLeft: 8,
|
|
1111
|
+
opacity: .55
|
|
1112
|
+
} : {
|
|
1113
|
+
...PANEL_STYLE.btn,
|
|
1114
|
+
marginTop: 8,
|
|
1115
|
+
marginLeft: 8
|
|
1116
|
+
},
|
|
1117
|
+
disabled: busy,
|
|
1118
|
+
onClick: () => props.face.act(target, "recheck")
|
|
1119
|
+
}, ACTION_LABEL.recheck) : null, reason ? react.createElement("div", { style: PANEL_STYLE.reason }, reason) : null, react.createElement("code", { style: PANEL_STYLE.cmd }, manual));
|
|
991
1120
|
}
|
|
992
1121
|
//#endregion
|
|
993
1122
|
//#region src/health-contract.ts
|
|
@@ -1047,8 +1176,15 @@ window.__ModuleLoader__.load({
|
|
|
1047
1176
|
function phoneOf(channel) {
|
|
1048
1177
|
return channel.startsWith("/") ? channel.slice(1) : channel;
|
|
1049
1178
|
}
|
|
1050
|
-
/**
|
|
1051
|
-
|
|
1179
|
+
/** 整批取数的时限(毫秒),**不是**一家一份:六家的活是在**同一个宿主线程**上排队干的。
|
|
1180
|
+
*
|
|
1181
|
+
* 为什么不是「技能侧 spawn 上限 20 s + 一点余量」(票 #740 对抗式审查第二轮逮到):
|
|
1182
|
+
* 各家的宿主半用的是 `spawnSync`(各单品的 `src/bridge.ts` 里 `SPAWN_TIMEOUT_MS = 20_000`),
|
|
1183
|
+
* 它**阻塞**宿主的事件循环——六通电话同时打进来,真正干活的是一个接一个串起来的。
|
|
1184
|
+
* 于是「六家之和」才是这一批的真实上界:按一家 20 s 算最坏 120 s,常见情形(一家 1~3 s)约 10 s。
|
|
1185
|
+
* 60 s 取两者之间:给常见情形留足余量,又不至于让一次卡死把按钮按成两分钟不动。
|
|
1186
|
+
* 单家的错照样逐家报(那家页签里的体检表会写「体检超时(60s)…」)。 */
|
|
1187
|
+
const HEALTH_TIMEOUT_MS = 6e4;
|
|
1052
1188
|
function timeoutText() {
|
|
1053
1189
|
return "体检超时(" + String(Math.round(HEALTH_TIMEOUT_MS / 1e3)) + "s):技能没在时限内回话。";
|
|
1054
1190
|
}
|