@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 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 秒超时);`--file`/stdin 读任意日志。纯只读,不碰 patch/台账。
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
- if (args[i] === '--file') file = args[++i]
170
- else if (args[i].startsWith('--file=')) file = args[i].slice('--file='.length)
171
- else if (args[i] === '--profile') profile = args[++i]
172
- else if (args[i].startsWith('--profile=')) profile = args[i].slice('--profile='.length)
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}',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyzyn/dsh-safe",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "description": "dsh 启动保险丝:社区插件不兼容导致 dsh 启动失败时,自动禁用坏插件并重试",
5
5
  "type": "module",
6
6
  "license": "MIT",