Re-skins the whole app to the dark design comps and wires the new surfaces to
the backend (the /api proxy + auth + schemas are unchanged).
Design system:
- globals.css: remapped @theme tokens to the comp palette (#08080a base, coral
#ff6f61, status cyan/green/amber/purple/teal); token names preserved
- MeshMark: triangle + 3-node brand glyph; cm-flow/cm-blink/cm-halo keyframes
- marketing flipped light → dark
Backend (migration 0012):
- agents.model_binding (persisted on team deploy) + GET /api/claws/{id}/runtime-config
- routine_runs table + scheduler journaling + GET /api/routines/runs
- GET /api/claws/{id}/compartments (anatomy aggregate)
- GET /api/structure/stats (workspace counts)
Frontend:
- Landing: full dark marketing page (hero constellation, deploy ladder,
12-topology taxonomy, recursive execution, compare/Pareto, safety, self-host)
- Auth: dark split-panel AuthShell + comp LoginForm + Clerk SignIn themed dark
- Dashboard shell: TopBar (breadcrumb + live stats + deploy + user) + StatusBar
(runner/sandbox/doors); rail slimmed to 60px + 252px context column
- ConstellationCanvas (radial recursive) replaces the graph view in StructureCanvas;
selecting a claw opens ComputerPanel (apps/now-running/dock); RoutinesPanel
- Claw anatomy view (/claws/[id]/anatomy) from compartments + runtime-config
Co-Authored-By: Claude Opus 4.8 <[email protected]>
75 lines
2.0 KiB
Rust
75 lines
2.0 KiB
Rust
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<AppState>,
|
|
Authed(user): Authed,
|
|
Query(query): Query<RoutinesQuery>,
|
|
) -> Result<Json<Vec<Routine>>, 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<AppState>,
|
|
Authed(user): Authed,
|
|
Json(body): Json<CreateRoutineRequest>,
|
|
) -> Result<(StatusCode, Json<Routine>), 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<AppState>,
|
|
Authed(user): Authed,
|
|
) -> Result<Json<Vec<Value>>, ApiError> {
|
|
Ok(Json(
|
|
cm_db::repo::routine_runs::recent_for_workspace(
|
|
&state.pool,
|
|
user.workspace_id.as_uuid(),
|
|
20,
|
|
)
|
|
.await?,
|
|
))
|
|
}
|