use crate::cli::{EnvironmentArgs, EnvironmentCommands}; use crate::context::CliContext; use crate::utils::confirm::confirm_delete; use crate::utils::json::{ apply_merge_patch, is_json_shorthand, merge_workspace_id_arg, parse_optional_json, parse_required_json, require_id, validate_create_id, }; use crate::utils::schema::append_agent_hints; use crate::utils::workspace::resolve_workspace_id; use schemars::schema_for; use yaak_models::models::Environment; use yaak_models::util::UpdateSource; type CommandResult = std::result::Result; pub fn run(ctx: &CliContext, args: EnvironmentArgs) -> i32 { let result = match args.command { EnvironmentCommands::List { workspace_id } => list(ctx, workspace_id.as_deref()), EnvironmentCommands::Schema { pretty } => schema(pretty), EnvironmentCommands::Show { environment_id } => show(ctx, &environment_id), EnvironmentCommands::Create { workspace_id, name, json } => { create(ctx, workspace_id, name, json) } EnvironmentCommands::Update { json, json_input } => update(ctx, json, json_input), EnvironmentCommands::Delete { environment_id, yes } => delete(ctx, &environment_id, yes), }; match result { Ok(()) => 0, Err(error) => { eprintln!("Error: {error}"); 1 } } } fn schema(pretty: bool) -> CommandResult { let mut schema = serde_json::to_value(schema_for!(Environment)) .map_err(|e| format!("Failed to serialize environment schema: {e}"))?; append_agent_hints(&mut schema); let output = if pretty { serde_json::to_string_pretty(&schema) } else { serde_json::to_string(&schema) } .map_err(|e| format!("Failed to format environment schema JSON: {e}"))?; println!("{output}"); Ok(()) } fn list(ctx: &CliContext, workspace_id: Option<&str>) -> CommandResult { let workspace_id = resolve_workspace_id(ctx, workspace_id, "environment list")?; let environments = ctx .db() .list_environments_ensure_base(&workspace_id) .map_err(|e| format!("Failed to list environments: {e}"))?; if environments.is_empty() { println!("No environments found in workspace {}", workspace_id); } else { for environment in environments { println!("{} - {} ({})", environment.id, environment.name, environment.parent_model); } } Ok(()) } fn show(ctx: &CliContext, environment_id: &str) -> CommandResult { let environment = ctx .db() .get_environment(environment_id) .map_err(|e| format!("Failed to get environment: {e}"))?; let output = serde_json::to_string_pretty(&environment) .map_err(|e| format!("Failed to serialize environment: {e}"))?; println!("{output}"); Ok(()) } fn create( ctx: &CliContext, workspace_id: Option, name: Option, json: Option, ) -> CommandResult { let json_shorthand = workspace_id.as_deref().filter(|v| is_json_shorthand(v)).map(str::to_owned); let workspace_id_arg = workspace_id.filter(|v| !is_json_shorthand(v)); let payload = parse_optional_json(json, json_shorthand, "environment create")?; if let Some(payload) = payload { if name.is_some() { return Err("environment create cannot combine --name with JSON payload".to_string()); } validate_create_id(&payload, "environment")?; let mut environment: Environment = serde_json::from_value(payload) .map_err(|e| format!("Failed to parse environment create JSON: {e}"))?; let fallback_workspace_id = if workspace_id_arg.is_none() && environment.workspace_id.is_empty() { Some(resolve_workspace_id(ctx, None, "environment create")?) } else { None }; merge_workspace_id_arg( workspace_id_arg.as_deref().or(fallback_workspace_id.as_deref()), &mut environment.workspace_id, "environment create", )?; if environment.parent_model.is_empty() { environment.parent_model = "environment".to_string(); } let created = ctx .db() .upsert_environment(&environment, &UpdateSource::Sync) .map_err(|e| format!("Failed to create environment: {e}"))?; println!("Created environment: {}", created.id); return Ok(()); } let workspace_id = resolve_workspace_id(ctx, workspace_id_arg.as_deref(), "environment create")?; let name = name.ok_or_else(|| { "environment create requires --name unless JSON payload is provided".to_string() })?; let environment = Environment { workspace_id, name, parent_model: "environment".to_string(), ..Default::default() }; let created = ctx .db() .upsert_environment(&environment, &UpdateSource::Sync) .map_err(|e| format!("Failed to create environment: {e}"))?; println!("Created environment: {}", created.id); Ok(()) } fn update(ctx: &CliContext, json: Option, json_input: Option) -> CommandResult { let patch = parse_required_json(json, json_input, "environment update")?; let id = require_id(&patch, "environment update")?; let existing = ctx .db() .get_environment(&id) .map_err(|e| format!("Failed to get environment for update: {e}"))?; let updated = apply_merge_patch(&existing, &patch, &id, "environment update")?; let saved = ctx .db() .upsert_environment(&updated, &UpdateSource::Sync) .map_err(|e| format!("Failed to update environment: {e}"))?; println!("Updated environment: {}", saved.id); Ok(()) } fn delete(ctx: &CliContext, environment_id: &str, yes: bool) -> CommandResult { if !yes && !confirm_delete("environment", environment_id) { println!("Aborted"); return Ok(()); } let deleted = ctx .db() .delete_environment_by_id(environment_id, &UpdateSource::Sync) .map_err(|e| format!("Failed to delete environment: {e}"))?; println!("Deleted environment: {}", deleted.id); Ok(()) }