@hyzyn/dsh-safe 0.14.0 → 0.14.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/lib/i18n.js +4 -2
- package/lib/repair.js +30 -3
- package/lib/wrap.js +3 -2
- package/package.json +1 -1
package/lib/i18n.js
CHANGED
|
@@ -136,7 +136,8 @@ const ZH = {
|
|
|
136
136
|
repairAmbiguous: '[dsh-safe] {id} 在多个 profile 中都有隔离记录({profiles}),请用 --profile <名> 指定。',
|
|
137
137
|
repairUnsupported: '[dsh-safe] 该失败类型不支持自动修复(可修复范围:包解析失败、导出版本不匹配等重装/升级可能修复的问题)。原因: {reason}\n 可用 dsh-safe explain 查看解读。',
|
|
138
138
|
repairPlan: '[dsh-safe] 将在 profile {profile} 中修复 {name}:执行 {command},成功后自动摘除隔离行',
|
|
139
|
-
repairDuplicate: '[dsh-safe]
|
|
139
|
+
repairDuplicate: '[dsh-safe] 这是重复挂载问题(id: {id})。一键修复:dsh-safe repair {id}(自动移除冗余来源并恢复挂载)',
|
|
140
|
+
repairDuplicateManual: '[dsh-safe] 这是重复挂载问题(同一 id 被多个 bundle/行挂载),重装包无法修复。请从 profile 的 bundles 清单或 patch 中移除多余的挂载来源,然后 dsh-safe restore 恢复。',
|
|
140
141
|
repairDedupePick: '[dsh-safe] {id} 同时由以下 bundle 挂载,保留哪个来源?',
|
|
141
142
|
repairDedupeDefault: '(bundles 中先声明,缺省保留)',
|
|
142
143
|
repairDedupeChoose: '保留哪个?输入编号 [1]: ',
|
|
@@ -286,7 +287,8 @@ Notes:
|
|
|
286
287
|
repairAmbiguous: '[dsh-safe] {id} is quarantined in multiple profiles ({profiles}); specify one with --profile <name>.',
|
|
287
288
|
repairUnsupported: '[dsh-safe] this failure type cannot be auto-repaired (repairable: package-resolution failures, export mismatches — problems a reinstall/upgrade may fix). Reason: {reason}\n try dsh-safe explain for an interpretation.',
|
|
288
289
|
repairPlan: '[dsh-safe] repairing {name} in profile {profile}: running {command}, then the quarantine row is removed automatically',
|
|
289
|
-
repairDuplicate: '[dsh-safe]
|
|
290
|
+
repairDuplicate: '[dsh-safe] duplicate-mount issue (id: {id}). One-command fix: dsh-safe repair {id} (removes the redundant source and restores automatically)',
|
|
291
|
+
repairDuplicateManual: '[dsh-safe] duplicate-mount issue (the same id is mounted by multiple bundles/rows); reinstalling cannot fix it. Remove the redundant mount source from the profile bundles/patch, then run dsh-safe restore.',
|
|
290
292
|
repairDedupePick: '[dsh-safe] {id} is mounted by the following bundles; which source to keep?',
|
|
291
293
|
repairDedupeDefault: ' (declared first in bundles; default keep)',
|
|
292
294
|
repairDedupeChoose: 'which one to keep? number [1]: ',
|
package/lib/repair.js
CHANGED
|
@@ -98,7 +98,7 @@ async function repairEntry({ profile, entry, to = undefined, yes = false, tty =
|
|
|
98
98
|
if (DUPLICATE_RE.test(entry.reason ?? '')) {
|
|
99
99
|
const sources = bundleMountSources(collectKnownRows(profile), id)
|
|
100
100
|
if (sources.length < 2) {
|
|
101
|
-
log(t('
|
|
101
|
+
log(t('repairDuplicateManual'))
|
|
102
102
|
return { code: 1, outcome: 'skipped' }
|
|
103
103
|
}
|
|
104
104
|
const manifest = readJsonIfExists(profileManifestPath(profile))
|
|
@@ -433,13 +433,40 @@ export async function cmdRepairAndBoot(args, { boot, spawn = spawnSync, log = er
|
|
|
433
433
|
if (opts.profile && profile !== opts.profile) continue
|
|
434
434
|
for (const entry of entries ?? []) targets.push({ profile, entry })
|
|
435
435
|
}
|
|
436
|
+
|
|
437
|
+
// 预检:duplicate 失败不产生台账条目——按挂载来源找出未入账的重复挂载,
|
|
438
|
+
// 并入修复目标(否则 -r 对这类 profile 空手而归)
|
|
439
|
+
const scanProfiles = opts.profile ? [opts.profile] : [...new Set(targets.map((t) => t.profile))]
|
|
440
|
+
for (const p of scanProfiles) {
|
|
441
|
+
const known = collectKnownRows(p)
|
|
442
|
+
const ledgerIds = new Set(targets.filter((t) => t.profile === p).map((t) => t.entry.id))
|
|
443
|
+
const byId = new Map()
|
|
444
|
+
for (const row of known.rows) {
|
|
445
|
+
if (row.source === 'profile' || row.source === 'home') continue
|
|
446
|
+
if (!byId.has(row.id)) byId.set(row.id, new Set())
|
|
447
|
+
byId.get(row.id).add(row.source)
|
|
448
|
+
}
|
|
449
|
+
for (const [dupId, srcs] of byId) {
|
|
450
|
+
if (srcs.size < 2 || ledgerIds.has(dupId)) continue
|
|
451
|
+
const nameRow = known.rows.find((r) => r.id === dupId)
|
|
452
|
+
targets.unshift({ profile: p, entry: { id: dupId, name: nameRow?.name ?? null, reason: 'duplicate loader entry id' } })
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// --profile 同时作用于修复与启动:启动参数未显式给 --profile 时自动补上
|
|
457
|
+
const finalBootArgs =
|
|
458
|
+
opts.profile && !bootArgs.some((a) => a === '--profile' || a.startsWith('--profile='))
|
|
459
|
+
? ['--profile', opts.profile, ...bootArgs]
|
|
460
|
+
: bootArgs
|
|
461
|
+
|
|
436
462
|
if (!targets.length) {
|
|
437
463
|
log(t('noRecords'))
|
|
438
|
-
if (bootArgs.length) return boot(
|
|
464
|
+
if (bootArgs.length) return boot(finalBootArgs)
|
|
439
465
|
return 0
|
|
440
466
|
}
|
|
441
467
|
|
|
442
468
|
const { failed } = await repairBatch(targets, { to: opts.to, yes: opts.yes, dryRun: opts.dryRun, spawn, log, write })
|
|
443
|
-
|
|
469
|
+
|
|
470
|
+
if (bootArgs.length) return boot(finalBootArgs)
|
|
444
471
|
return failed ? 1 : 0
|
|
445
472
|
}
|
package/lib/wrap.js
CHANGED
|
@@ -119,9 +119,10 @@ export async function runWrapped(options) {
|
|
|
119
119
|
if (!dryRun) persistFailure(invocation.profile, stderr)
|
|
120
120
|
// 重复挂载(同一 id 被多个 bundle/行挂载)发生在 include 挂载阶段,
|
|
121
121
|
// 早于 profile 层 disabled 覆盖的合并——禁用行管不住它,隔离无效。
|
|
122
|
-
//
|
|
122
|
+
// 给出一键修复指引(repair 会移除冗余来源并恢复)并透传。
|
|
123
123
|
if (stderr.includes('duplicate loader entry id')) {
|
|
124
|
-
|
|
124
|
+
const m = /duplicate loader entry id: ([\w:\-]+)/.exec(stderr)
|
|
125
|
+
log(t('repairDuplicate', { id: m ? m[1] : '' }))
|
|
125
126
|
log(t('explainHint'))
|
|
126
127
|
return code
|
|
127
128
|
}
|