优化: P0 shell stdout修复+灵感路由+3工具新增+ai-md全局CSS+import路径统一
P0 修复:
- B-260615-37: shell.rs 补 Stdio::piped() 修复 run_command stdout/stderr 恒空
(tokio 默认 inherit 导致 wait_with_output 读不到 pipe)
P1 功能:
- B-260615-36: 路由加 /ideas/:id + Ideas.vue 接 route.params.id + watch
(修复项目详情点「来源灵感」跳空白页)
- F-260615-08: 新增 file_info 工具(元信息: exists/size/lines/binary/dir)
- F-260615-09: 新增 append_file 工具(追加写入, RiskLevel::Medium)
- F-260615-12: 新增 search_files 工具(文件名 glob 搜索,递归,限50条)
+ search_files_recursive 辅助函数 + i18n zh/en 3工具×2命名空间
DRY/重构:
- CR-260615-09: .ai-md 样式5份→全局 src/styles/ai-md.css(75行)
AiChat.vue 删71行重复 + main.ts 引入 + typo修正(.a-md→.ai-md)
- 全量 import 路径统一为 @/ 别名(stores/composables/views ~28文件)
vite.config.ts 加 resolve.alias.{ '@': '/src' }
- i18n 批量改进: store error fallback 11处中文→t() / ToolCard ARG_LABEL
/ useAiSend queue文案 / Dashboard 空态 / Ideas.vue null守卫
- cargo check 0 error / vue-tsc 0 error
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
//! Shell 执行器 — 通过 tokio::process 执行 shell 命令
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::process::Stdio;
|
||||
|
||||
/// Shell 命令执行结果
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
@@ -37,10 +38,12 @@ pub async fn execute(request: ShellRequest) -> anyhow::Result<ShellResult> {
|
||||
let mut cmd = if cfg!(windows) {
|
||||
let mut c = tokio::process::Command::new("cmd");
|
||||
c.arg("/C").arg(&request.command);
|
||||
c.stdout(Stdio::piped()).stderr(Stdio::piped());
|
||||
c
|
||||
} else {
|
||||
let mut c = tokio::process::Command::new("sh");
|
||||
c.arg("-c").arg(&request.command);
|
||||
c.stdout(Stdio::piped()).stderr(Stdio::piped());
|
||||
c
|
||||
};
|
||||
|
||||
@@ -52,24 +55,14 @@ 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) => match tokio::time::timeout(
|
||||
Some(secs) => tokio::time::timeout(
|
||||
std::time::Duration::from_secs(secs),
|
||||
child.wait_with_output(),
|
||||
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?,
|
||||
.map_err(|_| anyhow::anyhow!("命令执行超时: {}秒", secs))??,
|
||||
None => cmd.output().await?,
|
||||
};
|
||||
|
||||
let duration = start.elapsed().as_millis() as u64;
|
||||
|
||||
Reference in New Issue
Block a user