dsh-m 0.10.1 → 0.10.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/CHANGELOG.md +12 -0
- package/lib/client.js +2 -40
- package/lib/core/host-api.js +6 -4
- package/lib/core/profile-ops.js +30 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,12 @@ The full release history of dsh-m, maintained bilingually: **Chinese first, Engl
|
|
|
8
8
|
|
|
9
9
|
## 中文
|
|
10
10
|
|
|
11
|
+
### 0.10.2 修复:D3 自升级 post-write 伪失败账实改判 + 静默自升级(0.10.0→0.10.1 dogfood 实证轮)
|
|
12
|
+
|
|
13
|
+
- **需求(2026-10-18 凌晨,首次 D3 真机 dogfood——0.10.0→0.10.1 自升级事件)**:自升级报失败(`application=failed、error=already-installed`)但三面账(manifest/node_modules/lock)均已写入 0.10.1——宿主对自身 bundle 换装存在 **post-write 伪失败**:installBundle 写穿后 manifest 含该名,宿主完成路径自触守卫(自指操作伪影;守卫源码 `specPackageName` 对 tarball URL 同样可解析包名)。另:0.10.1 引入的自升级确认框破坏既有「点击即升级」体验(主人裁决撤除)。
|
|
14
|
+
- **方案**:① **D3 终态失败一律账实复读改判**——实读 listInstalled:目标版已写入 = 升级事实成立,按成功改判(needsRestart=true 提示重启激活,output 留改判痕);未到目标 = 真失败原语义;profileDir 自 host-api/两处缝透传。② **静默自升级**——确认框整体撤除(受控态/Modal/文案键/SSR 钉),角标点击直接升级,结局由成功/失败横幅如实报告。ADR-0022 增补决议 7(spike 推断修正 + 改判 + UX 裁决入册)。
|
|
15
|
+
- **验证**:typecheck 零错误;全量测试(新增 2 钉 T-D1 改判成功/T-D2 真失败双形态,静默结构钉替换确认框钉;18 例既有失败 028 本机环境清单零新增);真机事件本身即第一份 dogfood 数据:0.10.1 经「重启激活」到位,重启后守卫锚点干净。
|
|
16
|
+
|
|
11
17
|
### 0.10.1 修复:0.10.0 真机 E2E 实证缺陷三连——enable 竞速重试普适化 + 回装失败账实复读 + 守卫命中一律两步化
|
|
12
18
|
|
|
13
19
|
- **需求(2026-10-17,0.10.0 装机当日 E2E 实证,真机 skip-auth@0.4.1 与卸载→重装两个事件)**:①插件树重载窗口的 enable 阶段激活竞速(`pending (waiting for service: webRuntime)`)会让两步升级的第二腿、兜底回装腿与 D3 自升级通道直接终态失败——0.9.94 时代的「enable 失败自动重试一次(重试即愈)」本地增强只覆盖了首腿;②回装腿报失败 ≠ 真缺席——pnpm 可能已写入账面(激活阶段失败),此时建议「手动重装旧版」会误导用户降级;③卸载后同会话重装:宿主活跃态/manifest 字面仍视其在装(守卫命中),而 previousVersion 探测口径为「未安装」,0.10.0 的 previousVersion 前置门使守卫命中原样透传 already-installed。
|
|
@@ -673,6 +679,12 @@ The full release history of dsh-m, maintained bilingually: **Chinese first, Engl
|
|
|
673
679
|
|
|
674
680
|
## English
|
|
675
681
|
|
|
682
|
+
### Fixes in 0.10.2 — account-reality reconciliation for D3 self-upgrade post-write pseudo-failure + silent self-upgrade (first D3 dogfood round)
|
|
683
|
+
|
|
684
|
+
- **Need (2026-10-18 early morning, first real-machine D3 dogfood — the 0.10.0→0.10.1 self-upgrade event)**: the self-upgrade reported failure (`application=failed、error=already-installed`) while all three ledgers (manifest/node_modules/lock) had already been written to 0.10.1 — the host has a **post-write pseudo-failure** for its own bundle swap: installBundle writes through, the manifest then contains the name, and the host's completion path trips its own guard (self-referential artifact; the guard's `specPackageName` resolves package names from tarball URLs too). Separately: the 0.10.1 self-upgrade confirmation dialog broke the existing "click to upgrade" experience (owner ordered its removal).
|
|
685
|
+
- **Approach**: ① **reconcile on any D3 terminal failure** — re-read listInstalled: if the target version is already written, the upgrade factually happened and the result is re-judged as success (needsRestart=true prompting the activating restart, with the reconciliation noted in output); if the target is absent, the genuine-failure semantics stand; profileDir is threaded from host-api and both seams. ② **silent self-upgrade** — the confirmation dialog is removed wholesale (controlled state / modal / copy keys / SSR pin); the chip click upgrades directly and outcomes are still reported truthfully via banners. ADR-0022 decision 7 records the spike-inference correction, the reconciliation, and the UX ruling.
|
|
686
|
+
- **Verification**: typecheck clean; full suite (2 new pins T-D1 reconciled-success / T-D2 genuine-failure, silent structure pins replacing the confirmation pins; 18 pre-existing failures are the 028 machine-environment set, zero new); the real-machine event itself is the first dogfood datum: 0.10.1 landed via "restart to activate", guard journal anchors clean after restart.
|
|
687
|
+
|
|
676
688
|
### Fixes in 0.10.1 — three defects proven by same-day real-machine E2E of 0.10.0: enable-race retry universalized + account-reality re-read on rollback failure + guard hit always two-steps
|
|
677
689
|
|
|
678
690
|
- **Need (2026-10-17, proven during the same-day real-machine E2E of 0.10.0 — the skip-auth@0.4.1 upgrade event and the uninstall→reinstall event)**: ① the enable-stage activation race during plugin-tree reload windows (`pending (waiting for service: webRuntime)`) made the two-step upgrade's second leg, the rollback reinstall leg, and the D3 self-upgrade channel fail terminally — the 0.9.94-era "retry enable failures once (retry heals)" local enhancement covered only the first leg; ② a failed reinstall leg ≠ true absence — pnpm may have already written the package (activation failed afterwards), where advising "manually reinstall the old version" would mislead the user into a downgrade; ③ uninstall followed by a same-session reinstall: the host's live state / literal manifest still considers the package installed (guard fires) while the previousVersion probe reports "not installed", so 0.10.0's previousVersion precondition let the raw already-installed error surface.
|
package/lib/client.js
CHANGED
|
@@ -2351,9 +2351,6 @@ var ZH = {
|
|
|
2351
2351
|
"op.degraded": "\u5347\u7EA7\u5931\u8D25\uFF1A\u65B0\u7248\u5B89\u88C5\u672A\u6210\u529F\uFF0C\u5DF2\u81EA\u52A8\u6062\u590D\u65E7\u7248 {restoredTo}\u2014\u2014\u63D2\u4EF6\u5F53\u524D\u53EF\u7528\uFF0C\u53EF\u7A0D\u540E\u91CD\u8BD5\u3002",
|
|
2352
2352
|
"notify.selfupgraded": "dsh-m \u5DF2\u5347\u7EA7\uFF08v{from} \u2192 v{to}\uFF09",
|
|
2353
2353
|
"notify.selfupgraded.failed": "dsh-m \u5347\u7EA7\u5931\u8D25\uFF1A{err}",
|
|
2354
|
-
"selfupgrade.confirm": "\u5C06\u901A\u8FC7 tarball \u76F4\u88C5\u901A\u9053\u628A dsh-m \u5347\u7EA7\u5230 {version}\uFF1A\u4E0D\u7ECF\u8FC7\u5378\u8F7D\uFF0C\u5931\u8D25\u65F6\u5F53\u524D\u7248\u672C\u4E0D\u53D7\u5F71\u54CD\uFF1B\u5B8C\u6210\u540E\u9700\u91CD\u542F Desktop \u751F\u6548\u3002",
|
|
2355
|
-
"selfupgrade.confirm.noversion": "\u5C06\u901A\u8FC7 tarball \u76F4\u88C5\u901A\u9053\u5347\u7EA7 dsh-m\uFF1A\u4E0D\u7ECF\u8FC7\u5378\u8F7D\uFF0C\u5931\u8D25\u65F6\u5F53\u524D\u7248\u672C\u4E0D\u53D7\u5F71\u54CD\uFF1B\u5B8C\u6210\u540E\u9700\u91CD\u542F Desktop \u751F\u6548\u3002",
|
|
2356
|
-
"selfupgrade.go": "\u5347\u7EA7",
|
|
2357
2354
|
"failed.install": "\u5B89\u88C5\u5931\u8D25\uFF1A{err}",
|
|
2358
2355
|
"failed.uninstall": "\u5378\u8F7D\u5931\u8D25\uFF1A{err}",
|
|
2359
2356
|
"failed.upgrade": "\u5347\u7EA7\u5931\u8D25\uFF1A{err}",
|
|
@@ -2655,9 +2652,6 @@ var EN = {
|
|
|
2655
2652
|
"op.degraded": "Upgrade failed: the new version did not install \u2014 rolled back to {restoredTo}. The plugin still works; you can retry later.",
|
|
2656
2653
|
"notify.selfupgraded": "dsh-m upgraded (v{from} \u2192 v{to})",
|
|
2657
2654
|
"notify.selfupgraded.failed": "dsh-m upgrade failed: {err}",
|
|
2658
|
-
"selfupgrade.confirm": "Upgrade dsh-m to {version} via the tarball direct-install channel: no uninstall involved, the current version stays intact on failure; a Desktop restart is needed afterwards.",
|
|
2659
|
-
"selfupgrade.confirm.noversion": "Upgrade dsh-m via the tarball direct-install channel: no uninstall involved, the current version stays intact on failure; a Desktop restart is needed afterwards.",
|
|
2660
|
-
"selfupgrade.go": "Upgrade",
|
|
2661
2655
|
"failed.install": "Install failed: {err}",
|
|
2662
2656
|
"failed.uninstall": "Uninstall failed: {err}",
|
|
2663
2657
|
"failed.upgrade": "Upgrade failed: {err}",
|
|
@@ -5191,12 +5185,11 @@ var TABS = [
|
|
|
5191
5185
|
["installed", "tab.installed", "installed"],
|
|
5192
5186
|
["settings", "tab.settings", null]
|
|
5193
5187
|
];
|
|
5194
|
-
function DshmVersionChip({ version, notify, initialCheck, ops
|
|
5188
|
+
function DshmVersionChip({ version, notify, initialCheck, ops }) {
|
|
5195
5189
|
const [check, setCheck] = useState(
|
|
5196
5190
|
() => initialCheck !== void 0 ? initialCheck : typeof window !== "undefined" && window.localStorage ? readSelfCheckCache2(window.localStorage, { version }) : null
|
|
5197
5191
|
);
|
|
5198
5192
|
const [busy, setBusy] = useState(false);
|
|
5199
|
-
const [confirming, setConfirming] = useState(initialConfirm === true);
|
|
5200
5193
|
useEffect(() => {
|
|
5201
5194
|
if (initialCheck !== void 0) return;
|
|
5202
5195
|
let live = true;
|
|
@@ -5214,11 +5207,10 @@ function DshmVersionChip({ version, notify, initialCheck, ops, initialConfirm })
|
|
|
5214
5207
|
}, [initialCheck]);
|
|
5215
5208
|
const doUpgrade = async () => {
|
|
5216
5209
|
if (busy) return;
|
|
5217
|
-
|
|
5210
|
+
return performUpgrade();
|
|
5218
5211
|
};
|
|
5219
5212
|
const performUpgrade = async () => {
|
|
5220
5213
|
if (busy) return;
|
|
5221
|
-
setConfirming(false);
|
|
5222
5214
|
setBusy(true);
|
|
5223
5215
|
try {
|
|
5224
5216
|
const res = await (ops ? ops.runOp("upgrade", "dsh-m", async () => {
|
|
@@ -5247,36 +5239,6 @@ function DshmVersionChip({ version, notify, initialCheck, ops, initialConfirm })
|
|
|
5247
5239
|
};
|
|
5248
5240
|
const state = deriveChipState2(check, version);
|
|
5249
5241
|
if (state.kind === "outdated") {
|
|
5250
|
-
if (confirming) {
|
|
5251
|
-
return h(
|
|
5252
|
-
"div",
|
|
5253
|
-
{ className: "dshm-compat-overlay", ...backdropCloseHandlers2(() => setConfirming(false)) },
|
|
5254
|
-
h(
|
|
5255
|
-
"div",
|
|
5256
|
-
{ className: "dshm-compat-dialog", onClick: (e) => e.stopPropagation() },
|
|
5257
|
-
h("div", { className: "dshm-compat-title" }, lookup("selfupdate.available", { v: state.latest || "?" })),
|
|
5258
|
-
h(
|
|
5259
|
-
"div",
|
|
5260
|
-
{ className: "dshm-compat-body" },
|
|
5261
|
-
state.latest ? lookup("selfupgrade.confirm", { version: `v${state.latest}` }) : lookup("selfupgrade.confirm.noversion")
|
|
5262
|
-
),
|
|
5263
|
-
h(
|
|
5264
|
-
"div",
|
|
5265
|
-
{ className: "dshm-actions" },
|
|
5266
|
-
h(
|
|
5267
|
-
"button",
|
|
5268
|
-
{ className: "dshm-btn", onClick: () => setConfirming(false) },
|
|
5269
|
-
lookup("common.later")
|
|
5270
|
-
),
|
|
5271
|
-
h(
|
|
5272
|
-
"button",
|
|
5273
|
-
{ className: "dshm-btn primary", onClick: performUpgrade, disabled: busy },
|
|
5274
|
-
lookup("selfupgrade.go")
|
|
5275
|
-
)
|
|
5276
|
-
)
|
|
5277
|
-
)
|
|
5278
|
-
);
|
|
5279
|
-
}
|
|
5280
5242
|
return h(
|
|
5281
5243
|
"button",
|
|
5282
5244
|
{
|
package/lib/core/host-api.js
CHANGED
|
@@ -302,14 +302,16 @@ export function createApiDispatcher(ctx) {
|
|
|
302
302
|
}
|
|
303
303
|
case 'self-upgrade': {
|
|
304
304
|
assertWriteAllowed(profile, 'self-upgrade');
|
|
305
|
-
// 0.10.0 D3 自升级专用通道(desktop):tarball
|
|
306
|
-
//
|
|
307
|
-
//
|
|
308
|
-
//
|
|
305
|
+
// 0.10.0 D3 自升级专用通道(desktop):tarball 直装。0.10.2 实证修订:0.2.1 守卫经
|
|
306
|
+
// specPackageName 对 tarball URL 同样解析包名(自升级写穿后宿主完成路径自触守卫 = post-write
|
|
307
|
+
// 伪失败),由 D3 的账实复读改判兜住;通道分治不变——remove+registry-spec 通用两步仍不达自升级。
|
|
308
|
+
// 恒 panel 语义零外部日志(ADR-0018),intent 预写/收口在 core 层。
|
|
309
309
|
if (profile.kind !== 'web') {
|
|
310
310
|
const managed = await d.desktopSelfUpgradeViaTarball(ctx.pkg.name, ctx.pkg.version, {
|
|
311
311
|
timeoutMs: cfg().timeoutMs,
|
|
312
312
|
signal,
|
|
313
|
+
// 0.10.2:账实复读(post-write 伪失败改判)需要 profile 目录
|
|
314
|
+
profileDir: profile.dir,
|
|
313
315
|
}, {
|
|
314
316
|
getService: d.getService,
|
|
315
317
|
ensureService: d.ensureService,
|
package/lib/core/profile-ops.js
CHANGED
|
@@ -194,7 +194,7 @@ async function desktopInstallLocked(id, cfg, opts, deps) {
|
|
|
194
194
|
// 0.10.0 R6(install 臂):dsh-m 已装态下的 install 调用实为升级(守卫必拦)→ 改道 D3;
|
|
195
195
|
// 全新安装(未装)走既有单步——守卫不触发,R1「新装路径不动」。
|
|
196
196
|
if (pkg === SELF_PKG && previousVersion !== undefined) {
|
|
197
|
-
return desktopSelfUpgradeViaTarballLocked(pkg, previousVersion, { timeoutMs, signal: opts.signal }, {
|
|
197
|
+
return desktopSelfUpgradeViaTarballLocked(pkg, previousVersion, { timeoutMs, signal: opts.signal, profileDir: opts.profileDir }, {
|
|
198
198
|
getService: () => service,
|
|
199
199
|
npmLatest: deps.npmLatest,
|
|
200
200
|
npmTarballUrl: deps.npmTarballUrl,
|
|
@@ -671,6 +671,34 @@ async function desktopSelfUpgradeViaTarballLocked(pkgName, currentVersion, opts,
|
|
|
671
671
|
change = changeOf(await service.installBundle(spec, { enabled: true }));
|
|
672
672
|
}
|
|
673
673
|
if (isTerminalFailure(change)) {
|
|
674
|
+
// 0.10.2(D3 dogfood 实证,0.10.0→0.10.1 真机事件):宿主对自身 bundle 换装存在 post-write 伪失败——
|
|
675
|
+
// installBundle 已写穿三面账(manifest/node_modules/lock 均为目标版)却回传 failed/already-installed
|
|
676
|
+
// (自指操作伪影:写完后 manifest 含该名,宿主完成路径自触守卫)。终态失败一律实读账面:
|
|
677
|
+
// 目标版已写入 = 升级事实成立,按成功改判(needsRestart=true,重启激活);否则真失败(原语义)。
|
|
678
|
+
let installedVersion;
|
|
679
|
+
if (opts.profileDir && deps.listInstalled) {
|
|
680
|
+
try {
|
|
681
|
+
const row = (await deps.listInstalled(opts.profileDir)).items.find((it) => it.pkg === pkgName);
|
|
682
|
+
installedVersion = row?.version;
|
|
683
|
+
}
|
|
684
|
+
catch {
|
|
685
|
+
// 复读失败按真失败处理
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
if (installedVersion === latest.version) {
|
|
689
|
+
await resolveIntent({ ok: true, installedVersion: latest.version });
|
|
690
|
+
return {
|
|
691
|
+
id: pkgName,
|
|
692
|
+
pkg: pkgName,
|
|
693
|
+
spec,
|
|
694
|
+
version: latest.version,
|
|
695
|
+
buildApprovals: Array.isArray(change.approvedBuilds) ? change.approvedBuilds : [],
|
|
696
|
+
fallbackAllBuilds: false,
|
|
697
|
+
needsRestart: true,
|
|
698
|
+
output: `official pluginManager (self-upgrade via tarball): application=${change.application ?? 'failed'}、error=${change.error?.code ?? 'terminal-failure'} 经账实复读改判成功——目标版 ${latest.version} 已写入(宿主自升级 post-write 伪失败),重启 Desktop 激活`,
|
|
699
|
+
via: 'desktop-manager',
|
|
700
|
+
};
|
|
701
|
+
}
|
|
674
702
|
const pending = pendingOf(change);
|
|
675
703
|
if (change.application === 'cancelled') {
|
|
676
704
|
throw new DesktopOpsError('install-refused', `自升级已被取消(${pkgName}):当前版本 ${currentVersion} 不受影响`);
|
|
@@ -729,7 +757,7 @@ async function desktopUpgradeLocked(pkg, cfg, opts, deps) {
|
|
|
729
757
|
const selfTarget = items.find((it) => it.pkg === pkg);
|
|
730
758
|
if (!selfTarget)
|
|
731
759
|
throw new DesktopOpsError('install-refused', `desktop profile 未安装该插件: ${pkg}(升级目标必须是已装插件)`);
|
|
732
|
-
return desktopSelfUpgradeViaTarballLocked(pkg, selfTarget.version, { timeoutMs, signal: opts.signal }, {
|
|
760
|
+
return desktopSelfUpgradeViaTarballLocked(pkg, selfTarget.version, { timeoutMs, signal: opts.signal, profileDir: opts.profileDir }, {
|
|
733
761
|
getService: () => service,
|
|
734
762
|
npmLatest: deps.npmLatest,
|
|
735
763
|
npmTarballUrl: deps.npmTarballUrl,
|