@bruc3van/dsh-doctor 0.1.3 → 0.1.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.en.md +4 -2
- package/README.md +4 -2
- package/package.json +2 -2
- package/src/doctor.mjs +168 -20
- package/src/i18n.mjs +23 -11
package/README.en.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[中文](README.md) | English
|
|
4
4
|
|
|
5
|
-
DSH Doctor
|
|
5
|
+
DSH Doctor helps DSH and plugin users quickly identify plugins that break startup or stop working after a DSH upgrade. It groups each plugin's problems, impact, and recommended actions, while also checking common profile configuration and version-drift issues. Diagnosis is read-only by default; repairs run only after you explicitly use `--fix`, review the exact plan, and confirm it. File edits are backed up first.
|
|
6
6
|
|
|
7
7
|
This is a community-maintained third-party tool and is not an official DeepSeek project. It does not load or execute code from the plugins it inspects.
|
|
8
8
|
|
|
@@ -137,4 +137,6 @@ npm run check
|
|
|
137
137
|
node src/cli.mjs --help
|
|
138
138
|
```
|
|
139
139
|
|
|
140
|
-
The first publication of a new package must be performed by the npm account that owns the `@bruc3van` scope with `npm publish --access public`. Then configure a GitHub Actions Trusted Publisher in the npm package settings with Organization or user `bruc3van`, Repository `dsh-doctor`, Workflow filename `release.yml`, no Environment, and only the `npm publish` allowed action.
|
|
140
|
+
The first publication of a new package must be performed by the npm account that owns the `@bruc3van` scope with `npm publish --access public`. Then configure a GitHub Actions Trusted Publisher in the npm package settings with Organization or user `bruc3van`, Repository `dsh-doctor`, Workflow filename `release.yml`, no Environment, and only the `npm publish` allowed action.
|
|
141
|
+
|
|
142
|
+
Before each later release, add a Chinese `## vX.Y.Z` entry matching the version tag to `CHANGELOG.md`. Pushing a tag that matches `package.json` makes the workflow publish through OIDC with npm provenance and automatically create or update the GitHub Release from that Chinese entry. The release fails if the entry is missing or contains no Chinese text. No long-lived npm token is required.
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
中文 | [English](README.en.md)
|
|
4
4
|
|
|
5
|
-
DSH Doctor
|
|
5
|
+
DSH Doctor 面向 DSH 与插件使用者,帮助快速找出导致 DSH 启动异常或升级后不可用的插件,集中说明每个插件的问题、影响与处理方式,并检查常见的 profile 配置和版本漂移。诊断默认完全只读;只有显式使用 `--fix`、核对并确认精确的修复计划后才会执行,文件修改会先创建备份。
|
|
6
6
|
|
|
7
7
|
这是社区维护的第三方工具,不属于 DeepSeek 官方项目。它不会加载或执行待检查插件的代码。
|
|
8
8
|
|
|
@@ -137,4 +137,6 @@ npm run check
|
|
|
137
137
|
node src/cli.mjs --help
|
|
138
138
|
```
|
|
139
139
|
|
|
140
|
-
新包需要先由 `@bruc3van` 对应的 npm 账号完成一次 `npm publish --access public`,创建公开包页面。然后在 npm 包设置中添加 GitHub Actions Trusted Publisher:Organization or user 为 `bruc3van`,Repository 为 `dsh-doctor`,Workflow filename 为 `release.yml`,Environment 留空,Allowed actions 只启用 `npm publish
|
|
140
|
+
新包需要先由 `@bruc3van` 对应的 npm 账号完成一次 `npm publish --access public`,创建公开包页面。然后在 npm 包设置中添加 GitHub Actions Trusted Publisher:Organization or user 为 `bruc3van`,Repository 为 `dsh-doctor`,Workflow filename 为 `release.yml`,Environment 留空,Allowed actions 只启用 `npm publish`。
|
|
141
|
+
|
|
142
|
+
后续发布前,需要在 `CHANGELOG.md` 中增加与版本 tag 同名的中文 `## vX.Y.Z` 条目。推送与 `package.json` 版本一致的 tag 后,workflow 会通过 OIDC 发布 npm 包、生成 provenance,并自动用该中文条目创建或更新 GitHub Release;缺少中文条目时发布流程会失败。不需要保存长期 npm token。
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bruc3van/dsh-doctor",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.4",
|
|
4
|
+
"description": "Diagnose DSH plugins broken by upgrades, startup blockers, and profile issues with explicit, confirmed repairs",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"repository": {
|
package/src/doctor.mjs
CHANGED
|
@@ -39,6 +39,7 @@ function finding(severity, code, message, options = {}) {
|
|
|
39
39
|
message,
|
|
40
40
|
...options.package === undefined ? {} : { package: options.package },
|
|
41
41
|
...options.evidence === undefined ? {} : { evidence: options.evidence },
|
|
42
|
+
...options.details === undefined ? {} : { details: options.details },
|
|
42
43
|
...options.suggestion === undefined ? {} : { suggestion: options.suggestion },
|
|
43
44
|
...options.repair === undefined ? {} : { repair: options.repair },
|
|
44
45
|
}
|
|
@@ -293,7 +294,7 @@ function resolveHarnessContext(home, explicitRoot, findings) {
|
|
|
293
294
|
return { root: canonical, packages: new Map(), version: manifest?.version, authoritative: false }
|
|
294
295
|
}
|
|
295
296
|
|
|
296
|
-
findings.push(finding('warning', 'HARNESS_INSTALLATION_UNKNOWN', 'Could not locate the
|
|
297
|
+
findings.push(finding('warning', 'HARNESS_INSTALLATION_UNKNOWN', 'Could not locate the DSH installation currently used by this home.', {
|
|
297
298
|
evidence: sharedDsh,
|
|
298
299
|
suggestion: 'Pass --harness-root when diagnosing a source checkout.',
|
|
299
300
|
}))
|
|
@@ -556,7 +557,7 @@ function inspectClientPackage(record, context) {
|
|
|
556
557
|
? harnessPackages.get(supplier)
|
|
557
558
|
: harnessPackages.get(supplier) ?? resolvePackage(supplier)
|
|
558
559
|
if (supplied === undefined || supplied.manifest?.dsh?.client === undefined) {
|
|
559
|
-
findings.push(finding('error', 'CLIENT_EXTERNAL_WITHOUT_SUPPLIER', `${name} requests ${specifier}, but the active
|
|
560
|
+
findings.push(finding('error', 'CLIENT_EXTERNAL_WITHOUT_SUPPLIER', `${name} requests ${specifier}, but the active DSH has no client module supplier.`, {
|
|
560
561
|
package: name,
|
|
561
562
|
evidence: record.file,
|
|
562
563
|
suggestion: disableSuggestion,
|
|
@@ -569,7 +570,7 @@ function inspectClientPackage(record, context) {
|
|
|
569
570
|
for (const dependency of inject ?? []) {
|
|
570
571
|
if (!dependency.startsWith('@deepseek-ai/dsh-')) continue
|
|
571
572
|
if (!harnessPackages.has(stripClientSuffix(dependency))) {
|
|
572
|
-
findings.push(finding('error', 'REMOVED_CLIENT_INJECT', `${name} injects ${dependency}, which is absent from the active
|
|
573
|
+
findings.push(finding('error', 'REMOVED_CLIENT_INJECT', `${name} injects ${dependency}, which is absent from the active DSH.`, {
|
|
573
574
|
package: name,
|
|
574
575
|
evidence: record.file,
|
|
575
576
|
suggestion: disableSuggestion,
|
|
@@ -983,7 +984,7 @@ function inspectProfileHarnessPackages(profileDir, home, harness, findings) {
|
|
|
983
984
|
}
|
|
984
985
|
}
|
|
985
986
|
if (harness.authoritative && !harness.packages.has(name)) {
|
|
986
|
-
findings.push(finding('warning', 'STALE_PROFILE_HARNESS_PACKAGE', `${name} remains in the profile but is absent from the active
|
|
987
|
+
findings.push(finding('warning', 'STALE_PROFILE_HARNESS_PACKAGE', `${name} remains in the profile but is absent from the active DSH.`, {
|
|
987
988
|
package: name,
|
|
988
989
|
evidence: profileManifestFile,
|
|
989
990
|
suggestion: 'Reinstall the profile with the active DSH CLI and review plugins that still require this package.',
|
|
@@ -1005,7 +1006,7 @@ function inspectCompatibility(record, context) {
|
|
|
1005
1006
|
.filter(name => name.startsWith('@deepseek-ai/dsh-') && !harnessPackages.has(name))
|
|
1006
1007
|
.sort()
|
|
1007
1008
|
if (removedPeers.length > 0) {
|
|
1008
|
-
findings.push(finding('warning', 'LEGACY_HARNESS_PEERS', `${packageName}
|
|
1009
|
+
findings.push(finding('warning', 'LEGACY_HARNESS_PEERS', `${packageName} declares old interface packages that the active DSH has removed.`, {
|
|
1009
1010
|
package: packageName,
|
|
1010
1011
|
evidence: removedPeers.join(', '),
|
|
1011
1012
|
suggestion: 'Update this plugin before relying on it with the current DSH release.',
|
|
@@ -1017,7 +1018,7 @@ function inspectCompatibility(record, context) {
|
|
|
1017
1018
|
.filter(name => name.startsWith('@deepseek-ai/dsh-') && !harnessPackages.has(name))
|
|
1018
1019
|
.sort()
|
|
1019
1020
|
if (removedDependencies.length > 0) {
|
|
1020
|
-
findings.push(finding('warning', 'LEGACY_HARNESS_DEPENDENCIES', `${packageName} depends on Harness packages that no longer exist in the active
|
|
1021
|
+
findings.push(finding('warning', 'LEGACY_HARNESS_DEPENDENCIES', `${packageName} depends on Harness packages that no longer exist in the active DSH.`, {
|
|
1021
1022
|
package: packageName,
|
|
1022
1023
|
evidence: removedDependencies.join(', '),
|
|
1023
1024
|
suggestion: 'Update this plugin; its bundled DSH APIs may be incompatible with the current release.',
|
|
@@ -1041,13 +1042,31 @@ function inspectCompatibility(record, context) {
|
|
|
1041
1042
|
const version = supplier.manifest?.version
|
|
1042
1043
|
if (typeof version !== 'string' || semver.valid(version) === null) continue
|
|
1043
1044
|
if (semver.satisfies(version, range, { includePrerelease: true })) continue
|
|
1044
|
-
mismatches.push(
|
|
1045
|
+
mismatches.push({ name, required: range, active: version })
|
|
1045
1046
|
}
|
|
1046
1047
|
if (mismatches.length > 0) {
|
|
1047
|
-
|
|
1048
|
+
const grouped = new Map()
|
|
1049
|
+
for (const mismatch of mismatches) {
|
|
1050
|
+
const key = JSON.stringify([mismatch.required, mismatch.active])
|
|
1051
|
+
const group = grouped.get(key) ?? {
|
|
1052
|
+
required: mismatch.required, active: mismatch.active, packages: [],
|
|
1053
|
+
}
|
|
1054
|
+
group.packages.push(mismatch.name)
|
|
1055
|
+
grouped.set(key, group)
|
|
1056
|
+
}
|
|
1057
|
+
const groups = [...grouped.values()]
|
|
1058
|
+
.map(group => ({ ...group, packages: group.packages.sort() }))
|
|
1059
|
+
const evidence = groups.flatMap((group, index) => [
|
|
1060
|
+
...(groups.length > 1 ? [`Group ${String(index + 1)}:`] : []),
|
|
1061
|
+
`${groups.length > 1 ? ' ' : ''}Plugin requires: ${group.required}`,
|
|
1062
|
+
`${groups.length > 1 ? ' ' : ''}Active DSH: ${group.active}`,
|
|
1063
|
+
`${groups.length > 1 ? ' ' : ''}Affected ${String(group.packages.length)} package(s): ${group.packages.join(', ')}`,
|
|
1064
|
+
]).join('\n')
|
|
1065
|
+
findings.push(finding('warning', 'HARNESS_PEER_VERSION_MISMATCH', `${packageName} declares compatibility ranges that exclude the active DSH version.`, {
|
|
1048
1066
|
package: packageName,
|
|
1049
|
-
evidence
|
|
1050
|
-
|
|
1067
|
+
evidence,
|
|
1068
|
+
details: { peerVersionGroups: groups },
|
|
1069
|
+
suggestion: `Update ${packageName} to a release compatible with the active DSH.`,
|
|
1051
1070
|
repair: updateRepair(profile, packageName, commandRepair),
|
|
1052
1071
|
}))
|
|
1053
1072
|
}
|
|
@@ -1318,6 +1337,20 @@ function finish(context, findings) {
|
|
|
1318
1337
|
return { version: 1, ok: summary.errors === 0, context, summary, findings }
|
|
1319
1338
|
}
|
|
1320
1339
|
|
|
1340
|
+
function appendReportField(lines, label, value, indent = ' ') {
|
|
1341
|
+
const valueLines = String(value).split('\n')
|
|
1342
|
+
if (valueLines.length === 1) {
|
|
1343
|
+
lines.push(`${indent}${label}: ${valueLines[0]}`)
|
|
1344
|
+
return
|
|
1345
|
+
}
|
|
1346
|
+
lines.push(`${indent}${label}:`)
|
|
1347
|
+
lines.push(...valueLines.map(line => `${indent} ${line}`))
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
function uniqueStrings(values) {
|
|
1351
|
+
return [...new Set(values.filter(value => typeof value === 'string' && value.length > 0))]
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1321
1354
|
export function formatReport(report, options = {}) {
|
|
1322
1355
|
const color = options.color ?? false
|
|
1323
1356
|
const language = options.language ?? 'en'
|
|
@@ -1336,7 +1369,7 @@ export function formatReport(report, options = {}) {
|
|
|
1336
1369
|
'DSH Doctor',
|
|
1337
1370
|
`${zh ? 'Profile' : 'Profile'}: ${report.context.profile}`,
|
|
1338
1371
|
`${zh ? 'DSH 主目录' : 'Home'}: ${report.context.home}`,
|
|
1339
|
-
|
|
1372
|
+
`${zh ? '当前使用的 DSH' : 'Active DSH'}: ${report.context.harness.version ?? 'unknown'}${report.context.harness.root ? ` (${report.context.harness.root})` : ''}`,
|
|
1340
1373
|
`${zh ? 'DSH CLI' : 'DSH CLI'}: ${cliText}`,
|
|
1341
1374
|
`${zh ? 'Profile 插件' : 'Profile plugins'}: ${String(report.context.packages.length)}`,
|
|
1342
1375
|
zh
|
|
@@ -1345,19 +1378,130 @@ export function formatReport(report, options = {}) {
|
|
|
1345
1378
|
`${zh ? '输出语言' : 'Output language'}: ${languageName(language)}`,
|
|
1346
1379
|
'',
|
|
1347
1380
|
]
|
|
1348
|
-
|
|
1381
|
+
const packageNames = new Set(report.context.packages.map(item => item.name))
|
|
1382
|
+
const pluginFindings = new Map()
|
|
1383
|
+
const environmentFindings = []
|
|
1384
|
+
for (const item of report.findings) {
|
|
1385
|
+
if (item.package !== undefined && packageNames.has(item.package)) {
|
|
1386
|
+
const items = pluginFindings.get(item.package) ?? []
|
|
1387
|
+
items.push(item)
|
|
1388
|
+
pluginFindings.set(item.package, items)
|
|
1389
|
+
} else environmentFindings.push(item)
|
|
1390
|
+
}
|
|
1391
|
+
const problemPackages = report.context.packages
|
|
1392
|
+
.filter(item => (item.compatibility === 'incompatible' || item.compatibility === 'risk')
|
|
1393
|
+
&& pluginFindings.has(item.name))
|
|
1394
|
+
.sort((left, right) => {
|
|
1395
|
+
const rank = { incompatible: 0, risk: 1 }
|
|
1396
|
+
return rank[left.compatibility] - rank[right.compatibility] || left.name.localeCompare(right.name)
|
|
1397
|
+
})
|
|
1398
|
+
const unknownPackages = report.context.packages
|
|
1399
|
+
.filter(item => item.compatibility === 'unknown')
|
|
1400
|
+
.sort((left, right) => left.name.localeCompare(right.name))
|
|
1401
|
+
|
|
1402
|
+
if (report.findings.length === 0 && unknownPackages.length === 0) {
|
|
1349
1403
|
lines.push(paint('info', zh ? '正常 当前检查范围内未发现问题。' : 'OK No problems found by the MVP checks.'))
|
|
1350
1404
|
} else {
|
|
1351
|
-
|
|
1405
|
+
if (problemPackages.length > 0) {
|
|
1406
|
+
lines.push(zh
|
|
1407
|
+
? `插件问题(${String(problemPackages.length)} 个)`
|
|
1408
|
+
: `Plugin problems (${String(problemPackages.length)})`)
|
|
1409
|
+
lines.push('')
|
|
1410
|
+
for (const plugin of problemPackages) {
|
|
1411
|
+
const originals = pluginFindings.get(plugin.name)
|
|
1412
|
+
const status = plugin.compatibility === 'incompatible'
|
|
1413
|
+
? zh ? '不兼容' : 'INCOMPATIBLE'
|
|
1414
|
+
: zh ? '有风险' : 'RISK'
|
|
1415
|
+
const severity = plugin.compatibility === 'incompatible' ? 'error' : 'warning'
|
|
1416
|
+
lines.push(paint(severity, `[${status}] ${plugin.name}`))
|
|
1417
|
+
appendReportField(lines, zh ? '版本' : 'Version', plugin.installed === false
|
|
1418
|
+
? zh ? '未安装' : 'not installed'
|
|
1419
|
+
: plugin.version ?? (zh ? '未知' : 'unknown'))
|
|
1420
|
+
lines.push(zh
|
|
1421
|
+
? ` 问题(${String(originals.length)}):`
|
|
1422
|
+
: ` Problems (${String(originals.length)}):`)
|
|
1423
|
+
originals.forEach((original, index) => {
|
|
1424
|
+
const item = localizedFinding(original, language)
|
|
1425
|
+
const prefix = `${plugin.name} `
|
|
1426
|
+
const message = item.message.startsWith(prefix) ? item.message.slice(prefix.length) : item.message
|
|
1427
|
+
lines.push(` ${String(index + 1)}. [${item.code}] ${message}`)
|
|
1428
|
+
if (item.evidence !== undefined) appendReportField(
|
|
1429
|
+
lines, zh ? '证据' : 'Evidence', item.evidence, ' ',
|
|
1430
|
+
)
|
|
1431
|
+
})
|
|
1432
|
+
const suggestionGroups = new Map()
|
|
1433
|
+
for (const original of originals) {
|
|
1434
|
+
const suggestion = localizedFinding(original, language).suggestion
|
|
1435
|
+
if (suggestion === undefined) continue
|
|
1436
|
+
const key = original.repair?.id ?? `suggestion:${suggestion}`
|
|
1437
|
+
if (!suggestionGroups.has(key)) suggestionGroups.set(key, suggestion)
|
|
1438
|
+
}
|
|
1439
|
+
const suggestions = [...suggestionGroups.values()]
|
|
1440
|
+
if (suggestions.length > 0) {
|
|
1441
|
+
lines.push(` ${zh ? '处理建议' : 'Recommended actions'}:`)
|
|
1442
|
+
suggestions.forEach(item => lines.push(` - ${item}`))
|
|
1443
|
+
}
|
|
1444
|
+
const commands = uniqueStrings(originals
|
|
1445
|
+
.filter(item => item.repair?.kind === 'command')
|
|
1446
|
+
.map(item => item.repair.command.map(quote).join(' ')))
|
|
1447
|
+
if (commands.length > 0) {
|
|
1448
|
+
lines.push(` ${zh ? '可执行命令' : 'Available commands'}:`)
|
|
1449
|
+
commands.forEach(command => lines.push(` $ ${command}`))
|
|
1450
|
+
}
|
|
1451
|
+
lines.push('')
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
if (unknownPackages.length > 0) {
|
|
1456
|
+
lines.push(zh
|
|
1457
|
+
? `兼容性未确认(${String(unknownPackages.length)} 个插件)`
|
|
1458
|
+
: `Compatibility unknown (${String(unknownPackages.length)} plugin(s))`)
|
|
1459
|
+
for (const plugin of unknownPackages) {
|
|
1460
|
+
lines.push(`[${zh ? '未知' : 'UNKNOWN'}] ${plugin.name}${plugin.version ? ` ${plugin.version}` : ''}`)
|
|
1461
|
+
appendReportField(lines, zh ? '原因' : 'Reason', zh
|
|
1462
|
+
? '插件没有声明当前 DSH 的兼容范围。'
|
|
1463
|
+
: 'The plugin does not declare a compatibility range for the active DSH.')
|
|
1464
|
+
appendReportField(lines, zh ? '建议' : 'Action', zh
|
|
1465
|
+
? '升级 DSH 后请关注该插件的发布说明或向插件作者确认。'
|
|
1466
|
+
: 'After a DSH upgrade, review the plugin release notes or ask its author to confirm compatibility.')
|
|
1467
|
+
}
|
|
1468
|
+
lines.push('')
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
const stale = environmentFindings.filter(item => item.code === 'STALE_PROFILE_HARNESS_PACKAGE')
|
|
1472
|
+
const otherEnvironment = environmentFindings.filter(item => item.code !== 'STALE_PROFILE_HARNESS_PACKAGE')
|
|
1473
|
+
if (stale.length > 0 || otherEnvironment.length > 0) {
|
|
1474
|
+
lines.push(zh ? 'DSH 环境问题' : 'DSH environment problems')
|
|
1475
|
+
lines.push('')
|
|
1476
|
+
}
|
|
1477
|
+
if (stale.length > 0) {
|
|
1478
|
+
lines.push(paint('warning', zh
|
|
1479
|
+
? `[警告] [STALE_PROFILE_HARNESS_PACKAGE ×${String(stale.length)}] 检测到当前 DSH 已不再包含的 profile 残留包。`
|
|
1480
|
+
: `[WARN] [STALE_PROFILE_HARNESS_PACKAGE ×${String(stale.length)}] Profile packages remain that the active DSH no longer includes.`))
|
|
1481
|
+
lines.push(` ${zh ? '残留包' : 'Stale packages'}:`)
|
|
1482
|
+
for (const original of stale) {
|
|
1483
|
+
const item = localizedFinding(original, language)
|
|
1484
|
+
lines.push(` - ${item.package ?? (zh ? '未知包' : 'unknown package')}`)
|
|
1485
|
+
if (item.evidence !== undefined) appendReportField(
|
|
1486
|
+
lines, zh ? '位置' : 'Location', item.evidence, ' ',
|
|
1487
|
+
)
|
|
1488
|
+
}
|
|
1489
|
+
const suggestions = uniqueStrings(stale.map(item => localizedFinding(item, language).suggestion))
|
|
1490
|
+
suggestions.forEach(item => appendReportField(lines, zh ? '处理建议' : 'Recommended action', item))
|
|
1491
|
+
lines.push('')
|
|
1492
|
+
}
|
|
1493
|
+
for (const original of otherEnvironment) {
|
|
1352
1494
|
const item = localizedFinding(original, language)
|
|
1353
1495
|
const label = zh
|
|
1354
1496
|
? item.severity === 'error' ? '错误' : item.severity === 'warning' ? '警告' : '信息'
|
|
1355
|
-
: item.severity === 'error' ? 'ERROR' : item.severity === 'warning' ? 'WARN
|
|
1497
|
+
: item.severity === 'error' ? 'ERROR' : item.severity === 'warning' ? 'WARN' : 'INFO'
|
|
1356
1498
|
lines.push(paint(item.severity, `${label} [${item.code}] ${item.message}`))
|
|
1357
|
-
if (item.package !== undefined) lines
|
|
1358
|
-
if (item.evidence !== undefined) lines
|
|
1359
|
-
if (item.suggestion !== undefined) lines
|
|
1360
|
-
if (item.repair?.kind === 'command')
|
|
1499
|
+
if (item.package !== undefined) appendReportField(lines, zh ? '包' : 'Package', item.package)
|
|
1500
|
+
if (item.evidence !== undefined) appendReportField(lines, zh ? '证据' : 'Evidence', item.evidence)
|
|
1501
|
+
if (item.suggestion !== undefined) appendReportField(lines, zh ? '处理建议' : 'Recommended action', item.suggestion)
|
|
1502
|
+
if (item.repair?.kind === 'command') appendReportField(
|
|
1503
|
+
lines, zh ? '可执行命令' : 'Available command', `$ ${item.repair.command.map(quote).join(' ')}`,
|
|
1504
|
+
)
|
|
1361
1505
|
lines.push('')
|
|
1362
1506
|
}
|
|
1363
1507
|
}
|
|
@@ -1365,7 +1509,11 @@ export function formatReport(report, options = {}) {
|
|
|
1365
1509
|
? `汇总:${String(report.summary.errors)} 个错误,${String(report.summary.warnings)} 个警告`
|
|
1366
1510
|
: `Summary: ${String(report.summary.errors)} error(s), ${String(report.summary.warnings)} warning(s)`)
|
|
1367
1511
|
if (report.summary.errors > 0) lines.push(zh
|
|
1368
|
-
? '
|
|
1369
|
-
: '
|
|
1512
|
+
? 'DSH 可能无法启动。请优先更新或停用产生错误的插件。'
|
|
1513
|
+
: 'DSH may fail to start. Upgrade or disable the error-producing plugin first.')
|
|
1514
|
+
lines.push('')
|
|
1515
|
+
lines.push(zh
|
|
1516
|
+
? '本诊断结果由 @bruc3van/dsh-doctor 生成,仅供参考。欢迎在 GitHub Star 或反馈问题:https://github.com/bruc3van/dsh-doctor'
|
|
1517
|
+
: 'This diagnostic report was generated by @bruc3van/dsh-doctor for reference only. Star the project or share feedback on GitHub: https://github.com/bruc3van/dsh-doctor')
|
|
1370
1518
|
return `${lines.join('\n')}\n`
|
|
1371
1519
|
}
|
package/src/i18n.mjs
CHANGED
|
@@ -59,7 +59,7 @@ const ZH_MESSAGES = {
|
|
|
59
59
|
PACKAGE_NAME_MISMATCH: item => `${item.package ?? '依赖'}解析到了名称不匹配的包。`,
|
|
60
60
|
INVALID_HARNESS_ROOT: () => '指定的 Harness 根目录不是有效的源码工作区。',
|
|
61
61
|
INVALID_WORKSPACE_MANIFEST: () => '已忽略一个无效的 Harness workspace 包清单。',
|
|
62
|
-
HARNESS_INSTALLATION_UNKNOWN: () => '无法定位这个 DSH Home
|
|
62
|
+
HARNESS_INSTALLATION_UNKNOWN: () => '无法定位这个 DSH Home 当前使用的 DSH 安装。',
|
|
63
63
|
INVALID_DEPENDENCY_MAP: item => `${captured(item.message, /^(\S+) must be/) ?? '依赖字段'}必须是“包名到版本范围”的对象。`,
|
|
64
64
|
INVALID_CLIENT_DECLARATION: item => `${item.package} 的 dsh.client 声明无效。`,
|
|
65
65
|
INVALID_CLIENT_PLATFORM: item => `${item.package} 的 dsh.client.platform 必须是字符串。`,
|
|
@@ -71,10 +71,10 @@ const ZH_MESSAGES = {
|
|
|
71
71
|
CLIENT_BUNDLE_UNREADABLE: item => `无法读取 ${item.package} 的客户端 bundle。`,
|
|
72
72
|
UNDECLARED_CLIENT_REQUIRE: item => `${item.package} 引用了 ${captured(item.message, / requires (.+) but does not/) ?? '未声明模块'},但没有在 dsh.client.external 中声明。`,
|
|
73
73
|
REDUNDANT_CLIENT_EXTERNAL: item => `${item.package} 把平台模块 ${captured(item.message, / module (.+) as an external/) ?? ''} 重复声明为 external。`,
|
|
74
|
-
CLIENT_EXTERNAL_WITHOUT_SUPPLIER: item => `${item.package} 请求了 ${captured(item.message, / requests (.+), but/) ?? '客户端模块'}
|
|
75
|
-
REMOVED_CLIENT_INJECT: item => `${item.package} 注入了 ${captured(item.message, / injects (.+), which/) ?? '已移除的模块'}
|
|
76
|
-
LEGACY_HARNESS_PEERS: item => `${item.package}
|
|
77
|
-
LEGACY_HARNESS_DEPENDENCIES: item => `${item.package}
|
|
74
|
+
CLIENT_EXTERNAL_WITHOUT_SUPPLIER: item => `${item.package} 请求了 ${captured(item.message, / requests (.+), but/) ?? '客户端模块'},但当前使用的 DSH 没有对应的模块提供方。`,
|
|
75
|
+
REMOVED_CLIENT_INJECT: item => `${item.package} 注入了 ${captured(item.message, / injects (.+), which/) ?? '已移除的模块'},但当前使用的 DSH 中已不存在该模块。`,
|
|
76
|
+
LEGACY_HARNESS_PEERS: item => `${item.package} 声明依赖当前使用的 DSH 已移除的旧接口包。`,
|
|
77
|
+
LEGACY_HARNESS_DEPENDENCIES: item => `${item.package} 仍依赖当前使用的 DSH 中已不存在的旧包。`,
|
|
78
78
|
BUNDLE_NOT_INSTALLED: item => `配置中的 bundle ${item.package} 尚未安装。`,
|
|
79
79
|
BUNDLE_DECLARATION_MISSING: item => `${item.package} 被列为 profile bundle,但没有声明 dsh.bundle.patch。`,
|
|
80
80
|
BUNDLE_PATCH_MISSING: item => `${item.package} 的 bundle patch 文件缺失。`,
|
|
@@ -92,7 +92,7 @@ const ZH_MESSAGES = {
|
|
|
92
92
|
INVALID_CREDENTIALS_DOCUMENT: () => 'Harness 凭据文件无法解析。',
|
|
93
93
|
INVALID_CREDENTIALS_ROOT: () => 'Harness 凭据文件顶层必须是映射。',
|
|
94
94
|
INVALID_CREDENTIALS_LAYOUT: () => 'Harness 凭据文件不是受支持的 version 1 结构。',
|
|
95
|
-
HARNESS_PEER_VERSION_MISMATCH: item => `${item.package}
|
|
95
|
+
HARNESS_PEER_VERSION_MISMATCH: item => `${item.package} 声明的兼容范围不包含当前使用的 DSH 版本。`,
|
|
96
96
|
INVALID_PROFILE_NAME: item => `Profile 名称无效:${captured(item.message, /^Invalid profile name (.+)\.$/) ?? ''}`,
|
|
97
97
|
PROFILE_NOT_FOUND: item => `Profile ${captured(item.message, /^Profile (.+) does not exist\.$/) ?? ''} 不存在。`,
|
|
98
98
|
INVALID_DSH_CONFIGURATION: () => 'dsh 字段存在时必须是对象。',
|
|
@@ -114,7 +114,7 @@ const ZH_MESSAGES = {
|
|
|
114
114
|
LOCKFILE_DEPENDENCY_STALE: item => `pnpm lockfile 仍包含未声明的依赖 ${item.package}。`,
|
|
115
115
|
DSH_CLI_HARNESS_VERSION_MISMATCH: () => '当前 DSH CLI 与诊断到的 Harness 版本不一致。',
|
|
116
116
|
DUPLICATE_HARNESS_PACKAGE_VERSION: item => `${item.package} 在 profile 与共享 DSH 安装中存在不同版本。`,
|
|
117
|
-
STALE_PROFILE_HARNESS_PACKAGE: item => `${item.package} 残留在 profile
|
|
117
|
+
STALE_PROFILE_HARNESS_PACKAGE: item => `${item.package} 残留在 profile 中,但当前使用的 DSH 已不再包含它。`,
|
|
118
118
|
PROFILE_HARNESS_SCOPE_UNREADABLE: () => 'Profile 内的 @deepseek-ai 包作用域无法作为目录读取。',
|
|
119
119
|
}
|
|
120
120
|
|
|
@@ -155,7 +155,7 @@ const ZH_SUGGESTIONS = {
|
|
|
155
155
|
INVALID_SETTINGS_ROOT: () => '把顶层标量或数组替换为映射。',
|
|
156
156
|
INVALID_CREDENTIALS_DOCUMENT: () => '只修复报告的结构;Doctor 永远不会输出或重写秘密值。',
|
|
157
157
|
INVALID_CREDENTIALS_LAYOUT: () => '迁移文档结构,不要暴露或修改秘密值。',
|
|
158
|
-
HARNESS_PEER_VERSION_MISMATCH: item => `把 ${item.package}
|
|
158
|
+
HARNESS_PEER_VERSION_MISMATCH: item => `把 ${item.package} 更新到兼容当前使用的 DSH 的版本。`,
|
|
159
159
|
PROFILE_NOT_FOUND: () => '先启动一次该 profile,或用当前 DSH 安装初始化它。',
|
|
160
160
|
INVALID_DSH_CONFIGURATION: () => '启动 Harness 前,请先修复 dsh 配置对象。',
|
|
161
161
|
INVALID_PROFILE_CONFIGURATION: () => '启动 Harness 前,请先修复 dsh.profile 配置对象。',
|
|
@@ -188,9 +188,21 @@ export function localizedFinding(item, language) {
|
|
|
188
188
|
if (language !== 'zh') return item
|
|
189
189
|
const message = ZH_MESSAGES[item.code]?.(item) ?? item.message
|
|
190
190
|
const suggestion = item.suggestion === undefined ? undefined : (ZH_SUGGESTIONS[item.code]?.(item) ?? item.suggestion)
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
|
|
191
|
+
const peerGroups = item.details?.peerVersionGroups
|
|
192
|
+
const evidence = Array.isArray(peerGroups)
|
|
193
|
+
? peerGroups.flatMap((group, index) => [
|
|
194
|
+
...(peerGroups.length > 1 ? [`第 ${String(index + 1)} 组:`] : []),
|
|
195
|
+
`${peerGroups.length > 1 ? ' ' : ''}插件要求:${group.required}`,
|
|
196
|
+
`${peerGroups.length > 1 ? ' ' : ''}当前 DSH:${group.active}`,
|
|
197
|
+
`${peerGroups.length > 1 ? ' ' : ''}涉及 ${String(group.packages.length)} 个包:${group.packages.join('、')}`,
|
|
198
|
+
]).join('\n')
|
|
199
|
+
: typeof item.evidence === 'string'
|
|
200
|
+
? (item.code === 'LEGACY_HARNESS_PEERS' || item.code === 'LEGACY_HARNESS_DEPENDENCIES'
|
|
201
|
+
? item.evidence.replaceAll(', ', '、')
|
|
202
|
+
: item.evidence)
|
|
203
|
+
.replaceAll('(active ', '(当前 ')
|
|
204
|
+
.replace(/ at line (\d+), column (\d+)/g, ',第 $1 行第 $2 列')
|
|
205
|
+
: item.evidence
|
|
194
206
|
return { ...item, message, suggestion, evidence }
|
|
195
207
|
}
|
|
196
208
|
|