mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-09-14 23:22:51 +00:00
Port workflow version creation to Pebble native tools
This commit is contained in:
parent
ec81170c8a
commit
d611ef2bb1
4 changed files with 104 additions and 2 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -3313,6 +3313,7 @@ dependencies = [
|
|||
"fabro-api",
|
||||
"fabro-auth",
|
||||
"fabro-checkpoint",
|
||||
"fabro-client",
|
||||
"fabro-config",
|
||||
"fabro-core",
|
||||
"fabro-dump",
|
||||
|
|
@ -3337,6 +3338,7 @@ dependencies = [
|
|||
"fabro-validate",
|
||||
"fabro-vault",
|
||||
"fabro-workflow",
|
||||
"fabro-workflow-version",
|
||||
"futures",
|
||||
"git2",
|
||||
"hex",
|
||||
|
|
|
|||
|
|
@ -1858,7 +1858,10 @@ async fn mcp_workflow_version_validation_happens_before_auth_or_network() {
|
|||
serde_json::json!({"entrypoint":"workflow","files":{}}),
|
||||
)
|
||||
.await;
|
||||
assert_eq!(error, "entrypoint `workflow` is not present in workflow files");
|
||||
assert_eq!(
|
||||
error,
|
||||
"entrypoint `workflow` is not present in workflow files"
|
||||
);
|
||||
assert_mcp_run_tool_count(&client).await;
|
||||
client
|
||||
.shutdown()
|
||||
|
|
|
|||
|
|
@ -77,6 +77,8 @@ tempfile = "3"
|
|||
toml.workspace = true
|
||||
fabro-vault = { path = "../../foundation/fabro-vault" }
|
||||
[dev-dependencies]
|
||||
fabro-client = { path = "../../foundation/fabro-client" }
|
||||
fabro-workflow-version = { path = "../fabro-workflow-version" }
|
||||
fabro-llm = { path = "../fabro-llm", features = ["test-support"] }
|
||||
fabro-store = { path = "../fabro-store", features = ["test-support"] }
|
||||
fabro-auth = { path = "../../foundation/fabro-auth", features = ["test-support"] }
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
//! The Fabro run tools (`fabro_run_*`) as application tools a pebble coding
|
||||
//! The Fabro workflow and run tools as application tools a pebble coding
|
||||
//! agent can call.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
|
@ -66,6 +66,15 @@ pub(crate) async fn execute_fabro_run_tool(
|
|||
services: &FabroRunToolServices,
|
||||
) -> fabro_tool::ToolResult<String> {
|
||||
match name {
|
||||
fabro_tool::FABRO_WORKFLOW_VERSION_CREATE_TOOL_NAME => {
|
||||
let params =
|
||||
parse_fabro_tool_args::<fabro_tool::FabroWorkflowVersionCreateParams>(name, args)?;
|
||||
let source = fabro_tool::ValidatedWorkflowVersionCreate::try_from(params)?;
|
||||
let result =
|
||||
fabro_tool::create_workflow_version(Arc::clone(&services.backend), source).await?;
|
||||
let summary = fabro_tool::workflow_version_create_text(&result);
|
||||
render_fabro_tool_result(&summary, &result)
|
||||
}
|
||||
fabro_tool::FABRO_RUN_CREATE_TOOL_NAME => {
|
||||
let params = parse_fabro_tool_args::<fabro_tool::FabroRunCreateParams>(name, args)?;
|
||||
ensure_current_run_parent(¶ms, services.current_run_id)?;
|
||||
|
|
@ -196,3 +205,89 @@ where
|
|||
})?;
|
||||
Ok(format!("{summary}\n{json}"))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use fabro_tool::fabro_client::ClientBackend;
|
||||
use fabro_tool::{ValidatedWorkflowVersionCreate, WorkflowVersionPackager};
|
||||
use fabro_types::WorkflowVersion;
|
||||
use fabro_workflow_version::{CollectedWorkflowClosure, ValidatedWorkflowVersion};
|
||||
use serde_json::json;
|
||||
|
||||
use super::*;
|
||||
|
||||
struct SingleGraphPackager;
|
||||
|
||||
#[async_trait]
|
||||
impl WorkflowVersionPackager for SingleGraphPackager {
|
||||
async fn package(
|
||||
&self,
|
||||
source: ValidatedWorkflowVersionCreate,
|
||||
) -> anyhow::Result<CollectedWorkflowClosure> {
|
||||
let version = WorkflowVersion::new(source.entrypoint, source.files, BTreeMap::new())?;
|
||||
let id = version.id()?;
|
||||
Ok(CollectedWorkflowClosure::from_dependency_order(id, vec![(
|
||||
id,
|
||||
ValidatedWorkflowVersion::new(version)?,
|
||||
)]))
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn workflow_version_native_dispatch_registers_and_returns_version() {
|
||||
let server = httpmock::MockServer::start_async().await;
|
||||
let version = WorkflowVersion::new(
|
||||
"workflow".parse().unwrap(),
|
||||
BTreeMap::from([("workflow".parse().unwrap(), "digraph W {}".into())]),
|
||||
BTreeMap::new(),
|
||||
)
|
||||
.unwrap();
|
||||
let id = version.id().unwrap();
|
||||
let upload = server
|
||||
.mock_async(|when, then| {
|
||||
when.method(httpmock::Method::POST)
|
||||
.path("/api/v1/workflow-versions")
|
||||
.json_body_obj(&version);
|
||||
then.status(201)
|
||||
.json_body(json!({"workflow_version_id": id}));
|
||||
})
|
||||
.await;
|
||||
let client = fabro_client::Client::new_no_proxy(&server.url("")).unwrap();
|
||||
let services = FabroRunToolServices {
|
||||
backend: Arc::new(
|
||||
ClientBackend::new(Arc::new(client))
|
||||
.with_workflow_version_packager(Arc::new(SingleGraphPackager)),
|
||||
),
|
||||
current_run_id: "01KRBZW4DW0000000000000002".parse().unwrap(),
|
||||
base_cwd: "unused".into(),
|
||||
user_settings_path: "unused".into(),
|
||||
};
|
||||
let name = fabro_tool::FABRO_WORKFLOW_VERSION_CREATE_TOOL_NAME;
|
||||
assert_eq!(register_named_fabro_run_tools(&services, &[name]).len(), 1);
|
||||
let output = execute_fabro_run_tool(
|
||||
name,
|
||||
json!({"entrypoint":"workflow", "files":{"workflow":"digraph W {}"}}),
|
||||
&services,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let (summary, body) = output.split_once('\n').unwrap();
|
||||
assert_eq!(summary, format!("Registered workflow version {id}"));
|
||||
assert_eq!(
|
||||
serde_json::from_str::<serde_json::Value>(body).unwrap(),
|
||||
json!({"workflow_version_id": id})
|
||||
);
|
||||
let error = execute_fabro_run_tool(
|
||||
name,
|
||||
json!({"entrypoint":"missing", "files":{"workflow":"digraph W {}"}}),
|
||||
&services,
|
||||
)
|
||||
.await
|
||||
.unwrap_err();
|
||||
assert!(error.to_string().contains("not present"));
|
||||
upload.assert_calls_async(1).await;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue