修复: spawn panic 兜底 + namespace 淘汰提示 + run_command 超时与破坏命令信任门控

This commit is contained in:
lxy
2026-08-01 11:10:28 +08:00
parent 468950616b
commit c4ba920cf5
7 changed files with 145 additions and 11 deletions
+55 -1
View File
@@ -270,7 +270,21 @@ pub async fn run_workflow_inner(
// F-260616-06 ②-2: move task_id / target_status 进闭包供完成/失败回调使用
let cb_task_id = task_id.clone();
let cb_target_status = target_status.clone();
tauri::async_runtime::spawn(async move {
// panic-guard:执行 spawn 闭包最外层包 catch_unwind(AssertUnwindSafe(..))。
// panic 时走已存在 failed 分支(update status=failed + emit WorkflowFailed + state_registry.remove),
// 防 panic 后终态不发/registry 不清永久卡。主逻辑/调度零改动,仅在 panic 时补发终态。
// 注:依赖 panic=unwind(tokio 默认),若构建设 panic=abort 则 catch_unwind 不生效(尽力兜底)。
tauri::async_runtime::spawn({
// panic 兜底需要这些引用:move 进 catch_unwind 闭包前各取一份 clone 供 panic 分支使用。
let panic_exec_id = exec_id.clone();
let panic_db = db.clone();
let panic_event_bus = event_bus.clone();
let panic_registry = state_registry.clone();
async move {
use std::panic::AssertUnwindSafe;
use futures::FutureExt;
// 原闭包主体包进 catch_unwind:panic 以 Err(Box<dyn Any>) 返回,Ok 走正常路径。
let outcome = AssertUnwindSafe(async move {
let mut executor = DagExecutor::new(event_bus.clone(), exec_id.clone());
// 注册执行器状态机:StateMachine 内部 Arc<Mutex>clone 共享底层 HashMap
// cancel_workflow_node IPC 经 execution_id 取此引用 set_cancelled,直达运行中 HumanNode
@@ -364,6 +378,46 @@ pub async fn run_workflow_inner(
})
.await;
}
}) // 闭 catch_unwind 内 async move
.catch_unwind()
.await;
// panic 分支:catch_unwind 返回 Err(Box<dyn Any + Send>),走已存在 failed 清理路径。
match outcome {
Ok(_) => {}
Err(panic_payload) => {
// 提取 panic 消息(String/&'static str 常见,其他类型用兜底文案)
let msg = panic_payload
.downcast_ref::<String>()
.map(|s| s.clone())
.or_else(|| panic_payload.downcast_ref::<&'static str>().map(|s| s.to_string()))
.unwrap_or_else(|| "工作流执行 panic".to_string());
tracing::error!(
execution_id = %panic_exec_id,
"工作流执行 panic,走 failed 分支兜底: {}",
msg
);
let workflows = WorkflowRepo::new(&panic_db);
if let Err(e) = workflows.update_field(&panic_exec_id, "status", "failed").await {
tracing::error!("更新工作流状态失败(panic 兜底): {}", e);
}
if let Err(e) = workflows
.update_field(&panic_exec_id, "completed_at", &now_millis())
.await
{
tracing::error!("更新工作流完成时间失败(panic 兜底): {}", e);
}
panic_registry.lock().await.remove(&panic_exec_id);
panic_event_bus
.send(WorkflowEvent::WorkflowFailed {
execution_id: panic_exec_id.clone(),
error: msg,
failed_node: String::new(),
})
.await;
}
}
}
});
Ok(execution_id.to_string())