继续收敛插件宿主进程错误

保留插件工作区扫描与进程状态读取错误

保留插件停止 kill/wait 错误并修正主动终止退出码判定
This commit is contained in:
kdletters
2026-10-06 10:43:35 +08:00
parent 823be92284
commit 052f679965
2 changed files with 45 additions and 16 deletions
@@ -513,7 +513,8 @@ fn workspace_plugin_sources(root: &Path) -> Result<Vec<ScannedPluginSource>, Str
Err(error) => return Err(format!("读取插件工作区失败:{error}")),
};
let mut sources = Vec::new();
for entry in entries.flatten() {
for entry in entries {
let entry = entry.map_err(|error| format!("读取插件工作区目录项失败:{error}"))?;
let plugin_root = entry.path();
if !plugin_root.is_dir() || !plugin_root.join(PLUGIN_MANIFEST_FILE_NAME).is_file() {
continue;
@@ -976,10 +977,19 @@ impl PluginHost {
.as_ref()
.is_some_and(|existing| existing.status == "failed");
let mut running = existing.and_then(|existing| existing.running);
let exited = running
.as_mut()
.and_then(|running| running.child.try_wait().ok().flatten())
.is_some();
let process_error =
running
.as_mut()
.and_then(|running| match running.child.try_wait() {
Ok(Some(status)) => Some(if let Some(code) = status.code() {
format!("插件进程已退出:exitCode={code}")
} else {
"插件进程已被信号终止".to_string()
}),
Ok(None) => None,
Err(error) => Some(format!("读取插件进程状态失败:{error}")),
});
let exited = process_error.is_some();
if exited {
running = None;
}
@@ -1002,11 +1012,7 @@ impl PluginHost {
manifest,
root: source.root,
status,
last_error: if exited {
Some("插件进程已退出".to_string())
} else {
previous_error
},
last_error: process_error.or(previous_error),
running,
},
);
@@ -1186,17 +1192,37 @@ impl PluginHost {
.plugins
.get_mut(id)
.ok_or_else(|| "插件不存在".to_string())?;
let mut stop_error = None;
if let Some(mut running) = record.running.take() {
let _ = running.child.kill();
let _ = running.child.wait();
if let Err(error) = running.child.kill() {
stop_error = Some(format!("终止插件进程失败:{error}"));
}
match running.child.wait() {
Ok(_) if stop_error.is_none() => {}
Err(error) => {
stop_error = Some(format!("等待插件进程退出失败:{error}"));
}
_ => {}
}
}
record.status = if record.manifest.enabled {
record.status = if stop_error.is_some() {
"failed".to_string()
} else if record.manifest.enabled {
"stopped".to_string()
} else {
"disabled".to_string()
};
record.last_error = None;
audit(&root, id, "stop", true, None);
record.last_error = stop_error.clone();
audit(
&root,
id,
"stop",
stop_error.is_none(),
stop_error.as_deref(),
);
if let Some(error) = stop_error {
return Err(error);
}
self.summary_locked(record)
}
@@ -1214,7 +1240,9 @@ impl PluginHost {
}
let mut cleanup = Ok(());
if !enabled {
let _ = self.stop(id);
self.stop(id)
.map(|_| ())
.map_err(|error| format!("插件停止失败,未切换禁用状态:{error}"))?;
if let Some(editor) = crate::editor_adapters::ManagedEditor::for_plugin(id) {
let state = self
.state
@@ -99,3 +99,4 @@ DirectProject 回合失败在确认不是客户端内部不可归类故障时,
- 认证状态订阅底层原先先把 IPC 失败转成空注销函数,导致上层新增的错误提示也收不到;现让订阅拒绝向登录页传播,后台身份投影失败时清除身份并留痕。
- 发布构建预检原先把 `package.json` 元数据/读取/解析错误当成“没有 build”,把 `package-lock.json` 读取/解析错误当成“没有锁定版本”;现分别区分 NotFound 与真实读盘/JSON 错误,保留相对工作区路径和底层正文。
- 发布尝试账本和工作区偏好文件原先把损坏、权限、类型和 JSON 解析失败当成空账本/默认偏好;现只有文件不存在才使用默认,其他读取错误沿发布幂等和建项目录命令返回具体正文。
- 插件主机扫描工作区、读取进程状态、停止插件和禁用插件时原先吞掉目录项/`try_wait`/kill/wait 错误;现保留插件状态错误并阻止“停止失败却显示已禁用”,正常主动 kill 的非零退出码不再误报。