@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 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] 这是重复挂载问题(同一 id 被多个 bundle/行挂载),重装包无法修复。请从 profile 的 bundles 清单或 patch 中移除多余的挂载来源,然后 dsh-safe restore 恢复。',
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] this is a 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
+ 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('repairDuplicate'))
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(bootArgs)
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
- if (bootArgs.length) return boot(bootArgs)
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
- log(t('repairDuplicate'))
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyzyn/dsh-safe",
3
- "version": "0.14.0",
3
+ "version": "0.14.2",
4
4
  "description": "dsh 启动保险丝:社区插件不兼容导致 dsh 启动失败时,自动禁用坏插件并重试",
5
5
  "type": "module",
6
6
  "license": "MIT",