96 lines
2.8 KiB
Rust
96 lines
2.8 KiB
Rust
//! 脚本节点 — 执行 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,
|
||
shell_type: Default::default(),
|
||
};
|
||
|
||
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"
|
||
}
|
||
}
|