routa-server 0.14.1

Routa.js HTTP Server — axum adapter on top of routa-core
Documentation
use crate::state::AppState;

use super::{rpc_tool_result, tool_result_error, tool_result_json};

fn required_str_arg<'a>(
    args: &'a serde_json::Value,
    key: &str,
) -> Result<&'a str, serde_json::Value> {
    match args.get(key).and_then(|value| value.as_str()) {
        Some(value) if !value.trim().is_empty() => Ok(value),
        _ => Err(tool_result_error(&format!(
            "Missing required argument: {}",
            key
        ))),
    }
}

pub(super) async fn execute(
    state: &AppState,
    name: &str,
    args: &serde_json::Value,
    workspace_id: &str,
) -> Option<serde_json::Value> {
    let result = match name {
        "subscribe_to_events" => {
            let agent_id = args.get("agentId").and_then(|v| v.as_str()).unwrap_or("");
            let agent_name = args.get("agentName").and_then(|v| v.as_str()).unwrap_or("");
            let event_types: Vec<crate::events::AgentEventType> = args
                .get("eventTypes")
                .and_then(|v| v.as_array())
                .map(|arr| {
                    arr.iter()
                        .filter_map(|v| v.as_str())
                        .filter_map(crate::events::AgentEventType::from_str)
                        .collect()
                })
                .unwrap_or_default();

            let subscription_id = uuid::Uuid::new_v4().to_string();
            let subscription = crate::events::EventSubscription {
                id: subscription_id.clone(),
                agent_id: agent_id.to_string(),
                agent_name: agent_name.to_string(),
                event_types,
                exclude_self: true,
                one_shot: false,
                wait_group_id: None,
                priority: 0,
            };
            state.event_bus.subscribe(subscription).await;

            tool_result_json(&serde_json::json!({
                "success": true,
                "subscriptionId": subscription_id
            }))
        }
        "unsubscribe_from_events" => {
            let subscription_id = args
                .get("subscriptionId")
                .and_then(|v| v.as_str())
                .unwrap_or("");
            state.event_bus.unsubscribe(subscription_id).await;
            tool_result_json(&serde_json::json!({
                "success": true,
                "subscriptionId": subscription_id
            }))
        }
        "create_board" => match rpc_tool_result(
            state,
            "kanban.createBoard",
            serde_json::json!({
                "workspaceId": workspace_id,
                "name": args.get("name").and_then(|v| v.as_str()).unwrap_or("Board"),
                "columns": args.get("columns").cloned(),
            }),
        )
        .await
        {
            Ok(result) => {
                let board = result.get("board").cloned().unwrap_or_default();
                let columns = board
                    .get("columns")
                    .and_then(|value| value.as_array())
                    .cloned()
                    .unwrap_or_default()
                    .into_iter()
                    .map(|column| {
                        serde_json::json!({
                            "id": column.get("id").cloned().unwrap_or_default(),
                            "name": column.get("name").cloned().unwrap_or_default()
                        })
                    })
                    .collect::<Vec<_>>();
                tool_result_json(&serde_json::json!({
                    "boardId": board.get("id").cloned().unwrap_or_default(),
                    "name": board.get("name").cloned().unwrap_or_default(),
                    "columns": columns
                }))
            }
            Err(error) => tool_result_error(&error),
        },
        "list_boards" => match rpc_tool_result(
            state,
            "kanban.listBoards",
            serde_json::json!({ "workspaceId": workspace_id }),
        )
        .await
        {
            Ok(result) => {
                let boards = result
                    .get("boards")
                    .cloned()
                    .unwrap_or_else(|| serde_json::json!([]));
                tool_result_json(&boards)
            }
            Err(error) => tool_result_error(&error),
        },
        "get_board" => match rpc_tool_result(
            state,
            "kanban.getBoard",
            serde_json::json!({
                "boardId": args.get("boardId").and_then(|v| v.as_str()).unwrap_or("")
            }),
        )
        .await
        {
            Ok(result) => tool_result_json(&result),
            Err(error) => tool_result_error(&error),
        },
        "create_card" => match rpc_tool_result(
            state,
            "kanban.createCard",
            serde_json::json!({
                "workspaceId": workspace_id,
                "boardId": args.get("boardId").cloned(),
                "columnId": args.get("columnId").cloned(),
                "title": args.get("title").and_then(|v| v.as_str()).unwrap_or(""),
                "description": args.get("description").cloned(),
                "priority": args.get("priority").cloned(),
                "labels": args.get("labels").cloned(),
            }),
        )
        .await
        {
            Ok(result) => {
                let card = result
                    .get("card")
                    .cloned()
                    .unwrap_or_else(|| serde_json::json!({}));
                tool_result_json(&card)
            }
            Err(error) => tool_result_error(&error),
        },
        "move_card" => match rpc_tool_result(
            state,
            "kanban.moveCard",
            serde_json::json!({
                "cardId": args.get("cardId").and_then(|v| v.as_str()).unwrap_or(""),
                "targetColumnId": args.get("targetColumnId").and_then(|v| v.as_str()).unwrap_or(""),
                "position": args.get("position").cloned(),
            }),
        )
        .await
        {
            Ok(result) => {
                let card = result
                    .get("card")
                    .cloned()
                    .unwrap_or_else(|| serde_json::json!({}));
                tool_result_json(&card)
            }
            Err(error) => tool_result_error(&error),
        },
        "update_card" => match rpc_tool_result(
            state,
            "kanban.updateCard",
            serde_json::json!({
                "cardId": args.get("cardId").and_then(|v| v.as_str()).unwrap_or(""),
                "title": args.get("title").cloned(),
                "description": args.get("description").cloned(),
                "comment": args.get("comment").cloned(),
                "priority": args.get("priority").cloned(),
                "labels": args.get("labels").cloned(),
            }),
        )
        .await
        {
            Ok(result) => {
                let card = result
                    .get("card")
                    .cloned()
                    .unwrap_or_else(|| serde_json::json!({}));
                tool_result_json(&card)
            }
            Err(error) => tool_result_error(&error),
        },
        "delete_card" => match rpc_tool_result(
            state,
            "kanban.deleteCard",
            serde_json::json!({
                "cardId": args.get("cardId").and_then(|v| v.as_str()).unwrap_or("")
            }),
        )
        .await
        {
            Ok(result) => tool_result_json(&result),
            Err(error) => tool_result_error(&error),
        },
        "create_column" => match rpc_tool_result(
            state,
            "kanban.createColumn",
            serde_json::json!({
                "boardId": args.get("boardId").and_then(|v| v.as_str()).unwrap_or(""),
                "name": args.get("name").and_then(|v| v.as_str()).unwrap_or(""),
                "color": args.get("color").cloned(),
            }),
        )
        .await
        {
            Ok(result) => {
                let board = result.get("board").cloned().unwrap_or_default();
                let column = board
                    .get("columns")
                    .and_then(|value| value.as_array())
                    .and_then(|columns| columns.last())
                    .cloned()
                    .unwrap_or_default();
                tool_result_json(&serde_json::json!({
                    "columnId": column.get("id").cloned().unwrap_or_default(),
                    "name": column.get("name").cloned().unwrap_or_default(),
                    "position": column.get("position").cloned().unwrap_or_default()
                }))
            }
            Err(error) => tool_result_error(&error),
        },
        "delete_column" => match rpc_tool_result(
            state,
            "kanban.deleteColumn",
            serde_json::json!({
                "boardId": args.get("boardId").and_then(|v| v.as_str()).unwrap_or(""),
                "columnId": args.get("columnId").and_then(|v| v.as_str()).unwrap_or(""),
                "deleteCards": args.get("deleteCards").cloned(),
            }),
        )
        .await
        {
            Ok(result) => tool_result_json(&serde_json::json!({
                "deleted": result.get("deleted").cloned().unwrap_or(serde_json::json!(false)),
                "columnId": result.get("columnId").cloned().unwrap_or_default(),
                "cardsDeleted": result.get("cardsDeleted").cloned().unwrap_or(serde_json::json!(0)),
                "cardsMoved": result.get("cardsMoved").cloned().unwrap_or(serde_json::json!(0)),
            })),
            Err(error) => tool_result_error(&error),
        },
        "search_cards" => match rpc_tool_result(
            state,
            "kanban.searchCards",
            serde_json::json!({
                "workspaceId": workspace_id,
                "query": args.get("query").and_then(|v| v.as_str()).unwrap_or(""),
                "boardId": args.get("boardId").cloned(),
            }),
        )
        .await
        {
            Ok(result) => {
                let cards = result
                    .get("cards")
                    .cloned()
                    .unwrap_or_else(|| serde_json::json!([]));
                tool_result_json(&cards)
            }
            Err(error) => tool_result_error(&error),
        },
        "list_cards_by_column" => match rpc_tool_result(
            state,
            "kanban.listCardsByColumn",
            serde_json::json!({
                "workspaceId": workspace_id,
                "columnId": args.get("columnId").and_then(|v| v.as_str()).unwrap_or(""),
                "boardId": args.get("boardId").cloned(),
            }),
        )
        .await
        {
            Ok(result) => tool_result_json(&result),
            Err(error) => tool_result_error(&error),
        },
        "decompose_tasks" => match rpc_tool_result(
            state,
            "kanban.decomposeTasks",
            serde_json::json!({
                "workspaceId": workspace_id,
                "boardId": args.get("boardId").cloned(),
                "columnId": args.get("columnId").cloned(),
                "tasks": args.get("tasks").cloned().unwrap_or_else(|| serde_json::json!([])),
            }),
        )
        .await
        {
            Ok(result) => tool_result_json(&result),
            Err(error) => tool_result_error(&error),
        },
        "request_previous_lane_handoff" => {
            let task_id = match required_str_arg(args, "taskId") {
                Ok(value) => value,
                Err(error) => return Some(error),
            };
            let request_type = match required_str_arg(args, "requestType") {
                Ok(value) => value,
                Err(error) => return Some(error),
            };
            let request = match required_str_arg(args, "request") {
                Ok(value) => value,
                Err(error) => return Some(error),
            };
            let session_id = match required_str_arg(args, "sessionId") {
                Ok(value) => value,
                Err(error) => return Some(error),
            };
            match rpc_tool_result(
                state,
                "kanban.requestPreviousLaneHandoff",
                serde_json::json!({
                    "taskId": task_id,
                    "requestType": request_type,
                    "request": request,
                    "sessionId": session_id,
                }),
            )
            .await
            {
                Ok(result) => tool_result_json(&result),
                Err(error) => tool_result_error(&error),
            }
        }
        "submit_lane_handoff" => {
            let task_id = match required_str_arg(args, "taskId") {
                Ok(value) => value,
                Err(error) => return Some(error),
            };
            let handoff_id = match required_str_arg(args, "handoffId") {
                Ok(value) => value,
                Err(error) => return Some(error),
            };
            let status = match required_str_arg(args, "status") {
                Ok(value) => value,
                Err(error) => return Some(error),
            };
            let summary = match required_str_arg(args, "summary") {
                Ok(value) => value,
                Err(error) => return Some(error),
            };
            let session_id = match required_str_arg(args, "sessionId") {
                Ok(value) => value,
                Err(error) => return Some(error),
            };
            match rpc_tool_result(
                state,
                "kanban.submitLaneHandoff",
                serde_json::json!({
                    "taskId": task_id,
                    "handoffId": handoff_id,
                    "status": status,
                    "summary": summary,
                    "sessionId": session_id,
                }),
            )
            .await
            {
                Ok(result) => tool_result_json(&result),
                Err(error) => tool_result_error(&error),
            }
        }
        _ => return None,
    };

    Some(result)
}