bamboo_server/handlers/command/
handlers.rs1use actix_web::{web, HttpResponse};
2
3use crate::app_state::AppState;
4use crate::error::AppError;
5
6use super::sources::{list_mcp_tools_as_commands, list_workflows_as_commands, skill_to_command};
7use super::types::CommandListResponse;
8
9pub async fn list_commands(app_state: web::Data<AppState>) -> Result<HttpResponse, AppError> {
11 let mut commands = Vec::new();
12
13 match list_workflows_as_commands(&app_state.app_data_dir).await {
14 Ok(workflows) => commands.extend(workflows),
15 Err(error) => {
16 tracing::warn!("Failed to load workflows: {error}");
17 }
18 }
19
20 let skills = app_state
21 .skill_manager
22 .store()
23 .list_skills(None, false)
24 .await;
25 let skill_commands = skills.into_iter().map(|skill| skill_to_command(&skill));
26 commands.extend(skill_commands);
27
28 match list_mcp_tools_as_commands(app_state.get_ref()).await {
29 Ok(mcp_tools) => commands.extend(mcp_tools),
30 Err(error) => {
31 tracing::warn!("Failed to load MCP tools: {error}");
32 }
33 }
34
35 commands.sort_by(|left, right| left.name.cmp(&right.name));
36 Ok(HttpResponse::Ok().json(CommandListResponse {
37 total: commands.len(),
38 commands,
39 }))
40}
41
42pub async fn get_command(
44 app_state: web::Data<AppState>,
45 path: web::Path<(String, String)>,
46) -> Result<HttpResponse, AppError> {
47 let (command_type, id) = path.into_inner();
48
49 match command_type.as_str() {
50 "workflow" => {
51 let workflows_dir = app_state.app_data_dir.join("workflows");
52 let filename = format!("{id}.md");
53 let filepath = workflows_dir.join(&filename);
54
55 if !filepath.exists() {
56 return Err(AppError::NotFound(format!("Workflow {id} not found")));
57 }
58
59 let content = tokio::fs::read_to_string(&filepath)
60 .await
61 .map_err(|error| {
62 AppError::InternalError(anyhow::anyhow!("Failed to read workflow: {error}"))
63 })?;
64
65 Ok(HttpResponse::Ok().json(serde_json::json!({
66 "id": format!("workflow-{id}"),
67 "name": id,
68 "content": content,
69 "type": "workflow"
70 })))
71 }
72 "skill" => match app_state.skill_manager.store().get_skill(&id).await {
73 Ok(skill) => Ok(HttpResponse::Ok().json(skill)),
74 Err(error) => Err(AppError::NotFound(format!("Skill {id} not found: {error}"))),
75 },
76 "mcp" => Err(AppError::NotFound(
77 "MCP tools do not support content retrieval".to_string(),
78 )),
79 _ => Err(AppError::NotFound(format!(
80 "Unknown command type: {command_type}"
81 ))),
82 }
83}
84
85pub fn config(cfg: &mut web::ServiceConfig) {
87 cfg.route("/commands", web::get().to(list_commands))
88 .route("/commands/{command_type}/{id}", web::get().to(get_command));
89}