新增: 初始化 DevFlow 项目仓库
Tauri 2 + Vue 3 + Vite 6 桌面应用,Rust workspace 含 13 个 crate (df-ai / df-storage / df-workflow / df-core / df-execute 等)。 核心能力:AI 聊天 agentic 循环(工具调用+人工审批)、工作流引擎、 任务/想法/项目/阶段管理、可追溯性,及配套前端组件。
This commit is contained in:
94
crates/df-nodes/src/script_node.rs
Normal file
94
crates/df-nodes/src/script_node.rs
Normal file
@@ -0,0 +1,94 @@
|
||||
//! 脚本节点 — 执行 Shell 脚本或自定义命令
|
||||
|
||||
use async_trait::async_trait;
|
||||
use df_workflow::node::{Node, NodeContext, NodeOutput, NodeResult, NodeSchema};
|
||||
|
||||
/// 脚本节点
|
||||
pub struct ScriptNode;
|
||||
|
||||
#[async_trait]
|
||||
impl Node for ScriptNode {
|
||||
async fn execute(&self, ctx: NodeContext) -> NodeResult {
|
||||
tracing::info!("ScriptNode 执行: {}", ctx.node_id);
|
||||
|
||||
// 从 config 解析 command(必填)
|
||||
let command = ctx
|
||||
.config
|
||||
.get("command")
|
||||
.and_then(|v| v.as_str())
|
||||
.ok_or_else(|| anyhow::anyhow!("ScriptNode 缺少必填参数: command"))?;
|
||||
|
||||
// 解析可选参数
|
||||
let timeout_secs = ctx
|
||||
.config
|
||||
.get("timeout_secs")
|
||||
.and_then(|v| v.as_u64());
|
||||
|
||||
let working_dir = ctx
|
||||
.config
|
||||
.get("working_dir")
|
||||
.and_then(|v| v.as_str())
|
||||
.map(|s| s.to_string());
|
||||
|
||||
// 构建请求并执行
|
||||
let request = df_execute::shell::ShellRequest {
|
||||
command: command.to_string(),
|
||||
working_dir,
|
||||
env: std::collections::HashMap::new(),
|
||||
timeout_secs,
|
||||
};
|
||||
|
||||
tracing::info!("ScriptNode 执行命令: {}", command);
|
||||
let result = df_execute::shell::execute(request).await?;
|
||||
|
||||
// 非零退出码视为执行失败
|
||||
let exit_code = result.exit_code.unwrap_or(-1);
|
||||
if exit_code != 0 {
|
||||
anyhow::bail!(
|
||||
"脚本执行失败 (exit_code={}): {}",
|
||||
exit_code,
|
||||
result.stderr.trim()
|
||||
);
|
||||
}
|
||||
|
||||
tracing::info!(
|
||||
"ScriptNode 完成: 耗时 {}ms, exit_code={}",
|
||||
result.duration_ms,
|
||||
exit_code
|
||||
);
|
||||
|
||||
Ok(NodeOutput::from_value(serde_json::json!({
|
||||
"stdout": result.stdout,
|
||||
"stderr": result.stderr,
|
||||
"exit_code": exit_code,
|
||||
"duration_ms": result.duration_ms,
|
||||
})))
|
||||
}
|
||||
|
||||
fn schema(&self) -> NodeSchema {
|
||||
NodeSchema {
|
||||
params: serde_json::json!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"command": { "type": "string" },
|
||||
"timeout_secs": { "type": "integer" },
|
||||
"working_dir": { "type": "string" }
|
||||
},
|
||||
"required": ["command"]
|
||||
}),
|
||||
output: serde_json::json!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"stdout": { "type": "string" },
|
||||
"stderr": { "type": "string" },
|
||||
"exit_code": { "type": "integer" },
|
||||
"duration_ms": { "type": "integer" }
|
||||
}
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn node_type(&self) -> &str {
|
||||
"script"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user