重构: 后端 df-ai/commands 拆分+df-nodes/workflow 改造+P0 bug 修复

- df-ai: context 历史中毒三档自愈 sanitize_messages(AC3)+anthropic_compat tool_use_id None 跳过(AC1/AC2)+删 router/stream 死码
- df-core: events 加 select_type+decisions 多选审批契约(F-260615-01)
- df-execute: shell run_command 工具复用(F-260615-05)
- df-nodes: human_node 多选校验+2 端到端测(F-01)+取消跳 set_failed(B-03b-R1/R2/R8)
- df-workflow: executor/dag/state cancel 闭环(B-06/07/03a/b)+provider approve options(R-PD-5)
- df-storage: find_path_conflict 抽公共(R-PD-11)+COLS 常量断言
- df-ideas: 删 IdeaPromoter/PromotionPolicy 死码(R-PD-14)
- src-tauri/commands/ai: secret keyring 迁移(FR-S1/R-PD-4)+GeneratingGuard RAII+disarm(B-09/26)+newConversation 软复位(B-10)+stream 心跳/stop select/空回复判错(B-02/04/05/15)+run_command(F-05)+mask audit(AR-3)
- src-tauri/commands/{project,task,workflow,mod,lib,state}: task detail IPC(F-02)+approve decisions+task list 联动(B-29)
- Cargo.lock+Cargo.toml 依赖同步
This commit is contained in:
2026-06-15 05:14:42 +08:00
parent 04032a2a8d
commit 2de0c6ecb7
37 changed files with 2457 additions and 484 deletions

View File

@@ -52,14 +52,24 @@ pub async fn execute(request: ShellRequest) -> anyhow::Result<ShellResult> {
cmd.env(key, value);
}
// kill_on_drop 确保 child 句柄被 drop 时(包括 timeout 取消)自动 kill 子进程,
// 防止 stdout/stderr pipe fd 泄漏 + 孤儿/僵尸进程累积。
cmd.kill_on_drop(true);
let child = cmd.spawn()?;
// wait_with_output 消费 child 并回收所有 pipe + 等待退出(避免僵尸)。
let output = match request.timeout_secs {
Some(secs) => tokio::time::timeout(
Some(secs) => match tokio::time::timeout(
std::time::Duration::from_secs(secs),
cmd.output(),
child.wait_with_output(),
)
.await
.map_err(|_| anyhow::anyhow!("命令执行超时: {}秒", secs))??,
None => cmd.output().await?,
{
Ok(res) => res?,
// timeout 触发:此处 child 已被 wait_with_output 消费,但因 kill_on_drop
// 在构建时已设,spawn 出的底层进程在 child 句柄 drop 时自动 kill。
Err(_) => return Err(anyhow::anyhow!("命令执行超时: {}秒", secs)),
},
None => child.wait_with_output().await?,
};
let duration = start.elapsed().as_millis() as u64;