@1e0zj/dsh-plugin-mall 0.3.2 → 0.3.4
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 +76 -13
- package/cordis.patch.yml +13 -2
- package/package.json +1 -1
- package/src/cli.js +48 -0
- package/src/client.js +12 -4
- package/src/guard.js +470 -24
- package/src/index.js +304 -31
- package/src/installer.js +133 -20
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**An open plugin marketplace for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (dsh): search every GitHub repo tagged `topic:dsh-plugin`, automatically verify which ones are real dsh plugins, install and update with one click.**
|
|
4
4
|
|
|
5
|
-
[中文说明](#中文说明) · [Install](#install) · [Why another marketplace](#why-another-marketplace)
|
|
5
|
+
[中文说明](#中文说明) · [Install](#install) · [Why another marketplace](#why-another-marketplace) · [Roadmap](ROADMAP.md)
|
|
6
6
|
|
|
7
7
|
Two surfaces: a **Settings → Plugins → Marketplace** tab in the dsh web UI, and five agent tools usable from any session.
|
|
8
8
|
|
|
@@ -73,9 +73,11 @@ node <profile>/node_modules/@1e0zj/dsh-plugin-mall/src/cli.js guard launch --pro
|
|
|
73
73
|
|
|
74
74
|
**A plain `dsh web` already resolves pending installs.** The marketplace plugin runs
|
|
75
75
|
recovery when it loads: reaching that point proves dsh booted far enough to compose
|
|
76
|
-
the profile, so the pending marker is committed
|
|
77
|
-
fails validation
|
|
78
|
-
|
|
76
|
+
the profile, so the pending marker is committed — or rolled back, either because the
|
|
77
|
+
profile fails validation or because the install was left paused at the build-script
|
|
78
|
+
approval gate (an unapproved install is never committed, however healthy it looks).
|
|
79
|
+
Without this a single install would wedge the profile — every later install and
|
|
80
|
+
uninstall refuses while a marker is outstanding.
|
|
79
81
|
|
|
80
82
|
**`guard launch` is still strictly better**, because it also covers what a plain
|
|
81
83
|
start cannot: a plugin that boots fine and then crashes seconds later. It checks the
|
|
@@ -83,13 +85,42 @@ profile's pending-install marker before starting the command after `--`:
|
|
|
83
85
|
|
|
84
86
|
- **No pending install** — the command runs as-is, inheriting the terminal, and its exit code is preserved.
|
|
85
87
|
- **Clearly broken on disk** — the profile is rolled back to its pre-install snapshot *before* launch, then the command starts on the restored state.
|
|
86
|
-
- **Alive through the grace period** (default **10 seconds**; `--grace-ms <ms>` to change) — the pending snapshot is committed and the wrapper keeps waiting on the process.
|
|
88
|
+
- **Alive through the grace period** (default **10 seconds**; `--grace-ms <ms>` to change) — the pending snapshot is committed and the wrapper keeps waiting on the process. An install still paused at the approval gate is rolled back instead: surviving probation only proves the JS loads, not that you approved its build scripts.
|
|
87
89
|
- **Exits 0 inside the grace period** (one-shot command) — the pending snapshot is committed.
|
|
88
90
|
- **Crashes or exits nonzero inside the grace period** — the profile is rolled back and the *exact same command* is restarted once with the restored state (never in a loop); the restarted process's exit code is preserved. SIGINT/SIGTERM are forwarded to the child where the platform supports it; on Windows `.cmd`/`.bat` shims go through `%ComSpec%` with strict per-argument quoting.
|
|
89
91
|
|
|
90
92
|
Limitations: the grace window is the probation period — a failure that only surfaces **after** it (a plugin that crashes minutes in, or on a specific interaction) cannot be rolled back automatically, because committing deletes the active snapshot and `guard recover` then has nothing to restore. `guard validate` still diagnoses the on-disk state, but a post-commit failure needs manual repair — uninstall and reinstall the plugin, or restore a backup you kept separately. Both commands do only **static on-disk validation**; neither proves the plugin actually loads. A corrupt pending marker fails closed: the command is not launched and no unvalidated path is deleted. Preserve the snapshot and repair or restore a trustworthy marker, then run `guard recover`; quarantine the marker only after you have independently verified the profile, or decided to abandon automatic recovery.
|
|
91
93
|
|
|
92
94
|
|
|
95
|
+
## dsh won't start after an update? (affects 0.2.0 – 0.3.2, fixed in 0.3.3)
|
|
96
|
+
|
|
97
|
+
Symptom: `dsh web` exits with `cannot resolve profile bundle "<package>"`.
|
|
98
|
+
|
|
99
|
+
Cause: **updating an already-installed plugin** into a rollback (most often the
|
|
100
|
+
target carries build scripts and the flow paused at the approval card) could lose
|
|
101
|
+
the package — the rollback's reinstall of the old version was fooled by pnpm's
|
|
102
|
+
"Already up to date" short-circuit, so the plugin left node_modules while its
|
|
103
|
+
bundles declaration stayed. Fresh installs and removals are unaffected.
|
|
104
|
+
|
|
105
|
+
Recovery: add the package back exactly as the profile's `package.json` declares
|
|
106
|
+
it, then run `dsh web`.
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
# <profile> = %USERPROFILE%\.dsh\profiles\web or ~/.dsh/profiles/web
|
|
110
|
+
# npm package — package.json says "dsh-better-sidebar": "^0.13.1"
|
|
111
|
+
pnpm --dir <profile> add "dsh-better-sidebar@^0.13.1" --ignore-scripts
|
|
112
|
+
# GitHub source — package.json says "dsh-at-file": "github:omdsh-dev/dsh-at-file"
|
|
113
|
+
pnpm --dir <profile> add "github:omdsh-dev/dsh-at-file" --ignore-scripts
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
> Do not reach for 0.3.2's `dsh-plugin-guard recover` here. Its per-package
|
|
117
|
+
> fallback refuses `^` (a cmd metacharacter) and skips `github:` entirely, and
|
|
118
|
+
> pnpm writes nearly every dependency with one or the other — so the fallback
|
|
119
|
+
> never fires and recovery fails closed. Fixed on main: the fallback now pins the
|
|
120
|
+
> version (or commit) the lockfile resolved. Fixed in 0.3.3 — on that version
|
|
121
|
+
> `dsh-plugin-guard recover` does repair this.
|
|
122
|
+
|
|
123
|
+
|
|
93
124
|
## Agent tools
|
|
94
125
|
|
|
95
126
|
| Tool | What it does |
|
|
@@ -106,6 +137,8 @@ Limitations: the grace window is the probation period — a failure that only su
|
|
|
106
137
|
|
|
107
138
|
**dsh 插件市场** — 搜索 GitHub `dsh-plugin` 话题下的 DeepSeek Harness 插件仓库,自动验证哪些是真 dsh 插件,一键安装与更新。
|
|
108
139
|
|
|
140
|
+
[路线图](ROADMAP.md)(安全 > 便捷 > 精简,以及明确不做的)
|
|
141
|
+
|
|
109
142
|
与策展列表不同:**任何打上 `topic:dsh-plugin` 的仓库推送后立即可被发现**——无需投稿、无需审批。为保证开放性可用,做了这些事:
|
|
110
143
|
|
|
111
144
|
- **自动验证**:逐仓库拉取 `package.json`(jsDelivr/raw 双源 CDN,不占 API 配额),按官方 `dsh.bundle` / `dsh.client` 声明打徽章;默认"只看已验证"视图过滤约 73% 的话题噪音
|
|
@@ -191,22 +224,48 @@ node <profile>/node_modules/@1e0zj/dsh-plugin-mall/src/cli.js guard launch --pro
|
|
|
191
224
|
```
|
|
192
225
|
|
|
193
226
|
**普通的 `dsh web` 就会了结 pending 安装。** 本插件加载时即执行恢复——能加载
|
|
194
|
-
本身就证明 dsh 已经组装好 profile、启动到了这一步,于是提交 pending
|
|
195
|
-
|
|
196
|
-
|
|
227
|
+
本身就证明 dsh 已经组装好 profile、启动到了这一步,于是提交 pending 标记;两种
|
|
228
|
+
情况改为回滚:profile 校验不过,或者那次安装停在构建脚本批准闸而未获批准
|
|
229
|
+
(没批准的安装绝不提交,哪怕它看起来一切正常)。没有这一步的话,装完一个插件
|
|
230
|
+
就会把 profile 卡住:只要标记还在,之后所有安装和卸载都会被拒绝。
|
|
197
231
|
|
|
198
232
|
**`guard launch` 仍然更强**,因为它覆盖普通启动覆盖不了的情况:插件启动正常、
|
|
199
233
|
几秒后才崩。它在启动 `--` 之后的命令前检查该 profile 的 pending 安装标记:
|
|
200
234
|
|
|
201
235
|
- **无 pending 安装** —— 命令原样运行(继承终端),透传退出码;
|
|
202
236
|
- **静态校验明显过不了** —— 启动*之前*先把 profile 回滚到安装前快照,再在恢复后的状态上启动;
|
|
203
|
-
- **活过缓刑期**(默认 **10 秒**,`--grace-ms <ms>` 可调)—— 提交 pending
|
|
237
|
+
- **活过缓刑期**(默认 **10 秒**,`--grace-ms <ms>` 可调)—— 提交 pending 快照,包装器继续守候该进程;但仍停在批准闸的安装改为回滚:活过缓刑期只证明 JS 能加载,不证明你批准了它的构建脚本;
|
|
204
238
|
- **缓刑期内以 0 退出**(一次性命令)—— 同样提交 pending 快照;
|
|
205
239
|
- **缓刑期内崩溃或非零退出** —— 回滚 profile,并用恢复后的状态**原样重启同一命令一次**(绝不循环),透传重启进程的退出码。支持的平台会把 SIGINT/SIGTERM 转发给子进程;Windows 上 `.cmd`/`.bat` 经 `%ComSpec%` 启动,逐参数严格加引号。
|
|
206
240
|
|
|
207
241
|
限制:缓刑期就是观察期——**之后**才暴露的故障(跑了几分钟才崩、或某个特定操作才触发)无法自动回滚:提交会删掉当前快照,此时 `guard recover` 已无可恢复的东西。`guard validate` 仍能诊断落盘状态,但提交之后的故障只能手工修复——卸载并重装插件(或恢复你另行保留的备份)。两条命令都只做**静态落盘校验**,都不证明插件真的能加载。pending 标记损坏时关闭式失败:不启动命令、不删除任何未校验路径。要**保留快照**、修复或恢复一个可信的标记后再跑 `guard recover`;只有在你已经独立核实过 profile、或决定放弃自动恢复之后,才去隔离(删除/移走)标记。
|
|
208
242
|
|
|
209
243
|
|
|
244
|
+
## 升级后 dsh 起不来?(0.2.0 – 0.3.2 受影响,0.3.3 已修复)
|
|
245
|
+
|
|
246
|
+
症状:`dsh web` 报 `cannot resolve profile bundle "<包名>"` 直接退出。
|
|
247
|
+
|
|
248
|
+
原因:**更新已装插件**时若走到回滚(最常见:目标插件带构建脚本、停在批准卡),
|
|
249
|
+
回滚里「装回旧版本」的一步会被 pnpm 的 "Already up to date" 空转骗过——包从
|
|
250
|
+
node_modules 消失而 bundles 声明还在。新装、卸载不受影响。
|
|
251
|
+
|
|
252
|
+
恢复:照 profile `package.json` 里原本的写法把包装回去,然后 `dsh web`。
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
# <profile> = %USERPROFILE%\.dsh\profiles\web 或 ~/.dsh/profiles/web
|
|
256
|
+
# npm 包 —— package.json 里是 "dsh-better-sidebar": "^0.13.1"
|
|
257
|
+
pnpm --dir <profile> add "dsh-better-sidebar@^0.13.1" --ignore-scripts
|
|
258
|
+
# GitHub 源 —— package.json 里是 "dsh-at-file": "github:omdsh-dev/dsh-at-file"
|
|
259
|
+
pnpm --dir <profile> add "github:omdsh-dev/dsh-at-file" --ignore-scripts
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
> 别指望 0.3.2 的 `dsh-plugin-guard recover` 修这个:它的 per-package 兜底会
|
|
263
|
+
> 拒掉 `^`(cmd 转义符),`github:` 更是整个跳过,而 pnpm 存依赖几乎不是前者
|
|
264
|
+
> 就是后者——兜底一次也不会触发,恢复只会 fail-closed。main 上已修:兜底改钉
|
|
265
|
+
> lockfile 解析出的版本(或 commit)。0.3.3 已修复——那个版本的
|
|
266
|
+
> `dsh-plugin-guard recover` 确实能修这个故障。
|
|
267
|
+
|
|
268
|
+
|
|
210
269
|
## 工作原理
|
|
211
270
|
|
|
212
271
|
- 双面包(dual-face)插件:`dsh.bundle` 半边挂在 **host 平面**(profile bundle 层),
|
|
@@ -265,10 +324,14 @@ node <profile>/node_modules/@1e0zj/dsh-plugin-mall/src/cli.js guard launch --pro
|
|
|
265
324
|
三处。`allowBuilds` 是持久化的安全配置,所以安装最终失败时这次放宽会被
|
|
266
325
|
**撤销** —— 否则一个没装成的插件会让那个包名从此静默获得构建脚本执行权。
|
|
267
326
|
- 配置(`cordis.patch.yml` 中可改):`defaultProfile`(默认装进哪个 profile,
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
`
|
|
327
|
+
**不配则跟随本进程实际启动的那个** —— 从 loader 的配置树锚点 `ctx.baseUrl`
|
|
328
|
+
推导,那正是 profile 目录;推不出来才退回 `web` 并打日志)、`apiBase`
|
|
329
|
+
(GitHub API 地址)、`npmRegistry`(npm 查询源,留空则跟随 pnpm 实际安装
|
|
330
|
+
源)、`rawSources`(验证用的 package.json 源模板列表,`{repo}` 会替换成
|
|
331
|
+
owner/name,留空用内置的 jsDelivr + raw 双源)、`perPageMax`(搜索单页上限,
|
|
332
|
+
1–30 的整数,默认 30)、`allowRestart`(是否允许一键重启,默认 `true`)。
|
|
333
|
+
自动识别同时决定**启动恢复作用在哪个 profile 上**:能在此提交半装状态,
|
|
334
|
+
凭据是「本次启动成功了」,而那只能证明启动的那个 profile 是好的。
|
|
272
335
|
|
|
273
336
|
## 发布
|
|
274
337
|
|
package/cordis.patch.yml
CHANGED
|
@@ -5,8 +5,19 @@
|
|
|
5
5
|
- id: dsh-plugin-mall
|
|
6
6
|
name: '@1e0zj/dsh-plugin-mall'
|
|
7
7
|
config:
|
|
8
|
-
#
|
|
9
|
-
|
|
8
|
+
# defaultProfile is deliberately NOT set here.
|
|
9
|
+
#
|
|
10
|
+
# This file ships inside the package and is read as a bundle layer by
|
|
11
|
+
# every profile that installs the marketplace, so anything written here
|
|
12
|
+
# is an explicit config value in ALL of them. Pinning `defaultProfile:
|
|
13
|
+
# web` (as this file used to) meant a marketplace running in any other
|
|
14
|
+
# profile still defaulted its installs at web — and the value looked
|
|
15
|
+
# like the user's own choice, so nothing could tell it apart from one.
|
|
16
|
+
#
|
|
17
|
+
# Left unset, the plugin follows the profile this process actually
|
|
18
|
+
# booted (derived from the loader's config-tree anchor). Set it only to
|
|
19
|
+
# override that:
|
|
20
|
+
# defaultProfile: web
|
|
10
21
|
# GitHub REST API base (override for self-hosted mirrors/proxies).
|
|
11
22
|
apiBase: https://api.github.com
|
|
12
23
|
# npm registry for anti-squatting, update checks and the host-dependency
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -40,13 +40,18 @@ import { tmpdir } from "node:os";
|
|
|
40
40
|
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
41
41
|
import { fileURLToPath } from "node:url";
|
|
42
42
|
import {
|
|
43
|
+
clearPendingApprovalPause,
|
|
43
44
|
commitPendingSnapshot,
|
|
44
45
|
createProfileSnapshot,
|
|
46
|
+
describeRollbackRebuild,
|
|
45
47
|
listPendingSnapshots,
|
|
48
|
+
markPendingApprovalPause,
|
|
46
49
|
markPendingSnapshot,
|
|
50
|
+
pendingApprovalPaused,
|
|
47
51
|
pnpmGuardEnv,
|
|
48
52
|
preflightInstall,
|
|
49
53
|
readPendingSnapshot,
|
|
54
|
+
readValidatedPendingSnapshot,
|
|
50
55
|
recoverAll,
|
|
51
56
|
recoverProfile,
|
|
52
57
|
resolveDshHome,
|
|
@@ -380,6 +385,8 @@ function cmdRecover({ home, profileDir }) {
|
|
|
380
385
|
} else if (entry.action === "rolled-back") {
|
|
381
386
|
console.log(`ROLLED BACK ${scope}: ${(entry.issues ?? []).map((issueEntry) => issueEntry.title).join("; ") || "profile would not load"}`);
|
|
382
387
|
if (entry.removed?.length) console.log(` removed from node_modules: ${entry.removed.join(", ")}`);
|
|
388
|
+
const rebuild = describeRollbackRebuild(entry.rebuild);
|
|
389
|
+
if (rebuild !== undefined) console.log(` node_modules rebuild: ${rebuild}`);
|
|
383
390
|
} else if (entry.action === "none") {
|
|
384
391
|
console.log(`no pending ${scope}`);
|
|
385
392
|
} else {
|
|
@@ -693,9 +700,25 @@ async function runPlain(command, args) {
|
|
|
693
700
|
* Commit the pending snapshot once startup probation passes. A commit failure
|
|
694
701
|
* is a warning, not a launch failure — the process is already running and
|
|
695
702
|
* healthy, and the marker simply stays pending for the next launch.
|
|
703
|
+
*
|
|
704
|
+
* An approval-paused marker must never commit: the new version sits there with
|
|
705
|
+
* its build scripts never approved, so staying alive only proves the JS loads.
|
|
706
|
+
* Roll it back to the pre-install snapshot instead (a rollback failure keeps
|
|
707
|
+
* the marker for the next attempt, same as recoverProfile).
|
|
696
708
|
*/
|
|
697
709
|
function commitLaunchSnapshot(profileDir) {
|
|
698
710
|
try {
|
|
711
|
+
let pending;
|
|
712
|
+
try {
|
|
713
|
+
pending = readValidatedPendingSnapshot(profileDir);
|
|
714
|
+
} catch {
|
|
715
|
+
pending = undefined; // unreadable marker: leave it to guard recover
|
|
716
|
+
}
|
|
717
|
+
if (pending !== undefined && pendingApprovalPaused(pending) !== undefined) {
|
|
718
|
+
rollbackPendingSnapshot(profileDir);
|
|
719
|
+
console.log(`[guard] startup probation passed, but the install was abandoned at the approval gate — profile rolled back for ${profileDir}`);
|
|
720
|
+
return;
|
|
721
|
+
}
|
|
699
722
|
commitPendingSnapshot(profileDir);
|
|
700
723
|
console.log(`[guard] startup probation passed — pending snapshot committed for ${profileDir}`);
|
|
701
724
|
} catch (error) {
|
|
@@ -1156,6 +1179,31 @@ async function selfTest() {
|
|
|
1156
1179
|
const validated = validateInstalledProfile(profileDir);
|
|
1157
1180
|
if (validated.ok !== true) throw new Error("healthy profile should validate clean (cli)");
|
|
1158
1181
|
|
|
1182
|
+
// commitLaunchSnapshot: a marker paused at the approval gate must roll back
|
|
1183
|
+
// instead of committing even after a healthy probation — the candidate sits
|
|
1184
|
+
// there with its build scripts never approved, and committing would delete
|
|
1185
|
+
// the only rollback snapshot. Layout keeps the candidate a NEW dependency
|
|
1186
|
+
// so the rollback prunes node_modules without spawning pnpm.
|
|
1187
|
+
{
|
|
1188
|
+
const pauseProfile = join(root, "pause-home", "profiles", "web");
|
|
1189
|
+
mkdirSync(join(pauseProfile, "node_modules"), { recursive: true });
|
|
1190
|
+
writeFileSync(join(pauseProfile, "package.json"), JSON.stringify({ dependencies: {} }));
|
|
1191
|
+
writeFileSync(join(pauseProfile, "cordis.patch.yml"), "[]\n");
|
|
1192
|
+
const snap = createProfileSnapshot(pauseProfile, { fixture: true });
|
|
1193
|
+
markPendingSnapshot(snap, { spec: "good@2.0.0", preflight: { candidate: { name: "good", version: "2.0.0", kind: "bundle" } } });
|
|
1194
|
+
// 暂停现场:候选已装、声明已写,静态校验过得去——正是不许提交的原因。
|
|
1195
|
+
writeFileSync(join(pauseProfile, "package.json"), JSON.stringify({ dependencies: { good: "^2.0.0" } }));
|
|
1196
|
+
mkdirSync(join(pauseProfile, "node_modules", "good"), { recursive: true });
|
|
1197
|
+
writeFileSync(join(pauseProfile, "node_modules", "good", "package.json"), JSON.stringify({ name: "good", version: "2.0.0" }));
|
|
1198
|
+
markPendingApprovalPause(pauseProfile);
|
|
1199
|
+
commitLaunchSnapshot(pauseProfile);
|
|
1200
|
+
if (readPendingSnapshot(pauseProfile) !== undefined) throw new Error("commitLaunchSnapshot must consume (roll back) an approval-paused marker");
|
|
1201
|
+
if (existsSync(snap.dir)) throw new Error("the paused rollback must delete the snapshot dir");
|
|
1202
|
+
if (JSON.parse(readFileSync(join(pauseProfile, "package.json"), "utf8")).dependencies?.good !== undefined) {
|
|
1203
|
+
throw new Error("the paused rollback must restore the pre-install manifest");
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1159
1207
|
// guarded remove: exact official argv + shell:false runner seam, snapshot
|
|
1160
1208
|
// before mutation, immediate commit after a statically safe removal.
|
|
1161
1209
|
{
|
package/src/client.js
CHANGED
|
@@ -188,6 +188,7 @@ window.__ModuleLoader__.load({
|
|
|
188
188
|
status: snapshot.status,
|
|
189
189
|
detail: snapshot.detail,
|
|
190
190
|
needsApproval: snapshot.needsApproval,
|
|
191
|
+
staleOnRestart: snapshot.staleOnRestart,
|
|
191
192
|
approvalToken: snapshot.approvalToken,
|
|
192
193
|
kind: snapshot.kind,
|
|
193
194
|
output: output,
|
|
@@ -249,6 +250,7 @@ window.__ModuleLoader__.load({
|
|
|
249
250
|
spec: snap.spec,
|
|
250
251
|
detail: snap.detail,
|
|
251
252
|
needsApproval: snap.needsApproval,
|
|
253
|
+
staleOnRestart: snap.staleOnRestart,
|
|
252
254
|
approvalToken: snap.approvalToken,
|
|
253
255
|
kind: snap.kind,
|
|
254
256
|
output: entry.output || "",
|
|
@@ -259,9 +261,13 @@ window.__ModuleLoader__.load({
|
|
|
259
261
|
// 不在本次服务器列表里的条目属于上一次宿主会话(进程重启后
|
|
260
262
|
// tracker 清空)。已兑现的直接翻篇撤掉:completed 的重启已经
|
|
261
263
|
// 发生;needsApproval 暂停的批准卡片已随进程失效(事务由启动
|
|
262
|
-
//
|
|
263
|
-
//
|
|
264
|
-
//
|
|
264
|
+
// 恢复处置),留着只会让人点一个必然失败的按钮;staleOnRestart
|
|
265
|
+
// 的失败是「被另一个未了结事务挡住」,而那个事务必然已被启动恢复
|
|
266
|
+
// 处置——它的报错是现在时写的(「还没做完」「现在无法安装」),
|
|
267
|
+
// 留到重启之后会被当成当前状态读,而它描述的情形已经不存在。
|
|
268
|
+
// running 的标中断,别让轮询对着不存在的 id 空转。其余 failed
|
|
269
|
+
// 保留——网络、预检阻断这类原因重启后可能仍然成立,日志有排障
|
|
270
|
+
// 价值。这个判据不依赖 finishedAt(旧镜像里没有该字段)。
|
|
265
271
|
for (var key in next) {
|
|
266
272
|
if (serverIds[key]) continue;
|
|
267
273
|
var stale = next[key];
|
|
@@ -270,7 +276,9 @@ window.__ModuleLoader__.load({
|
|
|
270
276
|
status: "killed",
|
|
271
277
|
detail: "宿主进程已重启,该任务的记录随之丢失",
|
|
272
278
|
});
|
|
273
|
-
} else if (stale.status === "completed"
|
|
279
|
+
} else if (stale.status === "completed"
|
|
280
|
+
|| stale.staleOnRestart === true
|
|
281
|
+
|| (Array.isArray(stale.needsApproval) && stale.needsApproval.length > 0)) {
|
|
274
282
|
delete next[key];
|
|
275
283
|
}
|
|
276
284
|
}
|