@hyzyn/dsh-safe 0.8.0 → 0.9.0
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 +1 -1
- package/lib/ai.js +21 -7
- package/lib/i18n.js +4 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -99,7 +99,7 @@ Error: dsh: plugin tree failed to load: failed to apply loader entry smoke-broke
|
|
|
99
99
|
|
|
100
100
|
设置 `DSH_SAFE_AI_KEY` 后启用(默认对接 DeepSeek,OpenAI 兼容接口,可用 `DSH_SAFE_AI_BASE_URL` / `DSH_SAFE_AI_MODEL` 换任何兼容服务):
|
|
101
101
|
|
|
102
|
-
- **`dsh-safe explain [--profile <名> | --file <路径>]`**:默认解读最近一次启动失败(失败时 stderr 自动持久化到 `$DSH_HOME/dsh-safe/last-failure-<profile>.log`,人也可直接翻阅);`--profile` 现场试启该 profile 并解读(60
|
|
102
|
+
- **`dsh-safe explain [--profile <名> | --file <路径>] [-- <dsh 参数…>]`**:默认解读最近一次启动失败(失败时 stderr 自动持久化到 `$DSH_HOME/dsh-safe/last-failure-<profile>.log`,人也可直接翻阅);`--profile` 现场试启该 profile 并解读(60 秒超时,`--` 之后可透传 dsh 启动参数,如 `-- --port 3084`);`--file`/stdin 读任意日志。纯只读,不碰 patch/台账。未知参数严格报错,绝不静默吞。
|
|
103
103
|
- **AI 兜底识别**(`DSH_SAFE_AI_RECOVER=1`):正则特征识别不出坏插件时(如 dsh 升级换格式),让 AI 从 stderr 里挑元凶——**结果必须仍走同一验证管线**(对照真实 patch 行、第一方保护、dry-run 预览),命中不了照旧透传。仅在启动失败时调用。
|
|
104
104
|
- 隐私:发送前 home 路径脱敏为 `~`;AI 任何失败都静默降级。
|
|
105
105
|
|
package/lib/ai.js
CHANGED
|
@@ -110,9 +110,9 @@ export async function detectFailureWithAI(stderr, knownRows) {
|
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
/** 试启一个 profile 并捕获 stderr(60s 超时;超时留下的部分 stderr 也可解读)。 */
|
|
113
|
-
function bootProfileForExplain(profile, { spawn }) {
|
|
113
|
+
function bootProfileForExplain(profile, extraArgs, { spawn }) {
|
|
114
114
|
const target = resolveDshSpawnTarget('dsh')
|
|
115
|
-
const { status, stderr } = spawn(target.file, [...target.prefix, '--profile', profile], {
|
|
115
|
+
const { status, stderr } = spawn(target.file, [...target.prefix, '--profile', profile, ...extraArgs], {
|
|
116
116
|
stdio: ['ignore', 'ignore', 'pipe'],
|
|
117
117
|
env: process.env,
|
|
118
118
|
shell: target.shell,
|
|
@@ -165,11 +165,25 @@ export async function cmdExplain(args, {
|
|
|
165
165
|
}
|
|
166
166
|
let file
|
|
167
167
|
let profile
|
|
168
|
+
const bootArgs = []
|
|
169
|
+
let passthrough = false
|
|
168
170
|
for (let i = 0; i < args.length; i++) {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
171
|
+
const a = args[i]
|
|
172
|
+
if (passthrough) { bootArgs.push(a); continue }
|
|
173
|
+
if (a === '--') { passthrough = true; continue }
|
|
174
|
+
if (a === '--file') file = args[++i]
|
|
175
|
+
else if (a.startsWith('--file=')) file = args[i].slice('--file='.length)
|
|
176
|
+
else if (a === '--profile') profile = args[++i]
|
|
177
|
+
else if (a.startsWith('--profile=')) profile = args[i].slice('--profile='.length)
|
|
178
|
+
else {
|
|
179
|
+
// 严格解析:未知参数绝不静默吞(否则用户传给 dsh 的意图会被丢弃)
|
|
180
|
+
log(t('explainUnknownArg', { arg: a }))
|
|
181
|
+
return 2
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
if (bootArgs.length && profile === undefined) {
|
|
185
|
+
log(t('explainArgsNeedProfile'))
|
|
186
|
+
return 2
|
|
173
187
|
}
|
|
174
188
|
if (!aiEnabled()) {
|
|
175
189
|
log(t('aiDisabled'))
|
|
@@ -185,7 +199,7 @@ export async function cmdExplain(args, {
|
|
|
185
199
|
}
|
|
186
200
|
} else if (profile !== undefined) {
|
|
187
201
|
log(t('explainBooting', { profile }))
|
|
188
|
-
const { code, stderr } = bootProfileForExplain(profile, { spawn })
|
|
202
|
+
const { code, stderr } = bootProfileForExplain(profile, bootArgs, { spawn })
|
|
189
203
|
if (code === 0 || !stderr?.trim()) {
|
|
190
204
|
log(t('explainBootOk', { profile }))
|
|
191
205
|
return 0
|
package/lib/i18n.js
CHANGED
|
@@ -111,9 +111,11 @@ const ZH = {
|
|
|
111
111
|
aiRecovered: '[dsh-safe] AI 兜底识别出 {count} 个可疑坏插件(结果仍走同一隔离管线)',
|
|
112
112
|
explainHint: '[dsh-safe] 可运行 dsh-safe explain 解读这次失败',
|
|
113
113
|
explainBooting: '[dsh-safe] 正在试启 profile {profile}(60 秒超时;不修改任何文件)…',
|
|
114
|
+
explainArgsNeedProfile: '[dsh-safe] 透传的启动参数需要与 --profile 一起使用。',
|
|
114
115
|
explainBootOk: '[dsh-safe] profile {profile} 当前能正常启动,没有失败可解读。',
|
|
115
116
|
explainUsingLast: '[dsh-safe] 解读最近一次失败记录: {file}',
|
|
116
117
|
explainNoSource: '[dsh-safe] 没有可解读的内容。用法:dsh-safe explain [--profile <名> | --file <路径>],或从 stdin 管道输入。\n 包装启动失败时 stderr 会自动存到 $DSH_HOME/dsh-safe/last-failure-<profile>.log。',
|
|
118
|
+
explainUnknownArg: '[dsh-safe] explain 无法识别的参数: {arg}(透传给 dsh 的启动参数请放在 -- 之后,如 -- --port 3084)',
|
|
117
119
|
excludedByList: '[dsh-safe] 按豁免名单跳过 {label}(不自动禁用)',
|
|
118
120
|
doctorSelf: 'dsh-safe {version}',
|
|
119
121
|
doctorDsh: 'dsh {name} {version}',
|
|
@@ -247,9 +249,11 @@ Notes:
|
|
|
247
249
|
aiRecovered: '[dsh-safe] AI fallback identified {count} suspected broken plugin(s); the same quarantine pipeline applies',
|
|
248
250
|
explainHint: '[dsh-safe] run dsh-safe explain to interpret this failure',
|
|
249
251
|
explainBooting: '[dsh-safe] booting profile {profile} (60s timeout; no files are modified)…',
|
|
252
|
+
explainArgsNeedProfile: '[dsh-safe] passthrough boot args require --profile.',
|
|
250
253
|
explainBootOk: '[dsh-safe] profile {profile} boots fine right now — nothing failed to interpret.',
|
|
251
254
|
explainUsingLast: '[dsh-safe] interpreting the last failure record: {file}',
|
|
252
255
|
explainNoSource: '[dsh-safe] nothing to interpret. Usage: dsh-safe explain [--profile <name> | --file <path>], or pipe via stdin.\n On wrapped-boot failures stderr is saved to $DSH_HOME/dsh-safe/last-failure-<profile>.log automatically.',
|
|
256
|
+
explainUnknownArg: '[dsh-safe] unrecognized argument for explain: {arg} (put dsh boot args after --, e.g. -- --port 3084)',
|
|
253
257
|
excludedByList: '[dsh-safe] skipped {label} per the exclusion list (never auto-disabled)',
|
|
254
258
|
doctorSelf: 'dsh-safe {version}',
|
|
255
259
|
doctorDsh: 'dsh {name} {version}',
|