use axum::extract::{Query, State}; use axum::http::StatusCode; use axum::Json; use cm_db::repo::routines::Routine; use cm_domain::AgentId; use serde::Deserialize; use serde_json::{json, Value}; use crate::routes::claws::workspace_agent; use crate::{ApiError, AppState, Authed}; #[derive(Deserialize)] pub struct RoutinesQuery { #[serde(rename = "clawId")] claw_id: AgentId, } /// GET /api/routines?clawId= — the Routines app list (§7.6). pub async fn list( State(state): State, Authed(user): Authed, Query(query): Query, ) -> Result>, ApiError> { let agent = workspace_agent(&state, &user, query.claw_id).await?; Ok(Json( cm_db::repo::routines::list_by_agent(&state.pool, agent.id).await?, )) } #[derive(Deserialize)] pub struct CreateRoutineRequest { #[serde(rename = "clawId")] claw_id: AgentId, name: String, cron: String, message: String, } /// POST /api/routines — schedule a task (§7.6). pub async fn create( State(state): State, Authed(user): Authed, Json(body): Json, ) -> Result<(StatusCode, Json), ApiError> { let agent = workspace_agent(&state, &user, body.claw_id).await?; let next = cm_scheduler::next_occurrence(&body.cron, time::OffsetDateTime::now_utc()) .map_err(|_| ApiError::Conflict)?; let routine = cm_db::repo::routines::create( &state.pool, agent.id, &body.name, &body.cron, json!({"message": body.message}), next, ) .await?; Ok((StatusCode::CREATED, Json(routine))) } /// GET /api/routines/runs — recent routine firings across the workspace (the /// routines panel's run history). pub async fn runs( State(state): State, Authed(user): Authed, ) -> Result>, ApiError> { Ok(Json( cm_db::repo::routine_runs::recent_for_workspace( &state.pool, user.workspace_id.as_uuid(), 20, ) .await?, )) }