cli

Command-line interface for Radroots
git clone https://radroots.dev/git/cli.git
Log | Files | Refs | README | LICENSE

commit 4cb5dba0d772cb3500d60c18e6b37077ea77cd32
parent 1f7c1d99bb15de81dffb31bb2bd649cdd74104ee
Author: triesap <tyson@radroots.org>
Date:   Mon, 27 Apr 2026 07:37:27 +0000

cli: emit target command actions

Diffstat:
Msrc/operation_adapter.rs | 145+------------------------------------------------------------------------------
Msrc/operation_basket.rs | 7+------
Msrc/operation_market.rs | 33++++-----------------------------
Msrc/operation_order.rs | 41+----------------------------------------
Msrc/runtime/daemon.rs | 2+-
Msrc/runtime/farm.rs | 14+++++++-------
Msrc/runtime/find.rs | 4++--
Msrc/runtime/listing.rs | 65+++++++++++++++++++++++++++++------------------------------------
Msrc/runtime/local.rs | 6+++---
Msrc/runtime/network.rs | 2+-
Msrc/runtime/order.rs | 60+++++++++++++++++++++++++++++++++---------------------------
Msrc/runtime/sync.rs | 6+++---
Msrc/runtime/workflow.rs | 22++++++++++++----------
Mtests/signer_runtime_modes.rs | 10++++++++--
Mtests/support/mod.rs | 14++++++++++++++
Mtests/target_cli.rs | 16++++++++++++++++
16 files changed, 136 insertions(+), 311 deletions(-)

diff --git a/src/operation_adapter.rs b/src/operation_adapter.rs @@ -221,9 +221,8 @@ impl<P: OperationResultPayload> OperationResult<P> { &self, context: EnvelopeContext, ) -> Result<OutputEnvelope, OperationAdapterError> { - let mut result = serde_json::to_value(&self.payload) + let result = serde_json::to_value(&self.payload) .map_err(|error| OperationAdapterError::Serialization(error.to_string()))?; - translate_target_command_references(&mut result); Ok(OutputEnvelope { schema_version: OUTPUT_SCHEMA_VERSION, operation_id: self.operation_id().to_owned(), @@ -649,148 +648,6 @@ fn runtime_output_error( error } -fn translate_target_command_references(value: &mut Value) { - match value { - Value::String(value) => { - let translated = target_command_reference(value.as_str()); - if translated != *value { - *value = translated; - } - } - Value::Array(values) => { - for nested in values { - translate_target_command_references(nested); - } - } - Value::Object(object) => { - for nested in object.values_mut() { - translate_target_command_references(nested); - } - } - Value::Null | Value::Bool(_) | Value::Number(_) => {} - } -} - -fn target_command_reference(value: &str) -> String { - let action = target_command_action(value); - if action != value { - return action; - } - - let mut translated = value.to_owned(); - for (legacy, target) in [ - ("radroots config show", "radroots config get"), - ( - "radroots runtime config show", - "radroots runtime config get", - ), - ("radroots local init", "radroots store init"), - ("radroots local status", "radroots store status get"), - ("radroots local export", "radroots store export"), - ("radroots local backup", "radroots store backup create"), - ("radroots market update", "radroots market refresh"), - ("radroots market search", "radroots market product search"), - ("radroots market view", "radroots market listing get"), - ("radroots sync status", "radroots sync status get"), - ("radroots order ls", "radroots order list"), - ("radroots order history", "radroots order event list"), - ("radroots order watch", "radroots order event watch"), - ( - "radroots order create --listing", - "radroots basket item add", - ), - ("radroots order new", "radroots basket create"), - ("radroots order create", "radroots basket create"), - ("radroots rpc status", "radroots runtime status get"), - ("radroots rpc sessions", "radroots job list"), - ("radroots net status", "radroots relay list"), - ("radroots myc status", "radroots signer status get"), - ("radroots doctor", "radroots health check run"), - ("radroots status", "radroots health status get"), - ("radroots setup seller", "radroots farm create"), - ("radroots setup buyer", "radroots basket create"), - ("radroots setup both", "radroots workspace init"), - ("radroots sell add", "radroots listing create"), - ("radroots sell check", "radroots listing validate"), - ("radroots sell publish", "radroots listing publish"), - ("radroots sell update", "radroots listing update"), - ("radroots sell pause", "radroots listing archive"), - ("radroots sell show", "radroots listing get"), - ] { - translated = translated.replace(legacy, target); - } - translated -} - -fn target_command_action(action: &str) -> String { - match action { - "radroots config show" => "radroots config get".to_owned(), - "radroots runtime config show" => "radroots runtime config get".to_owned(), - "radroots local init" => "radroots store init".to_owned(), - "radroots local status" => "radroots store status get".to_owned(), - "radroots local export" => "radroots store export".to_owned(), - "radroots local backup" => "radroots store backup create".to_owned(), - "radroots market update" => "radroots market refresh".to_owned(), - "radroots sync status" => "radroots sync status get".to_owned(), - "radroots order ls" => "radroots order list".to_owned(), - "radroots order history" => "radroots order event list".to_owned(), - "radroots order new" | "radroots order create" => "radroots basket create".to_owned(), - "radroots rpc status" => "radroots runtime status get".to_owned(), - "radroots rpc sessions" => "radroots job list".to_owned(), - "radroots net status" => "radroots relay list".to_owned(), - "radroots myc status" => "radroots signer status get".to_owned(), - "radroots doctor" => "radroots health check run".to_owned(), - "radroots status" => "radroots health status get".to_owned(), - "radroots setup seller" => "radroots farm create".to_owned(), - "radroots setup buyer" => "radroots basket create".to_owned(), - "radroots setup both" => "radroots workspace init".to_owned(), - other if other.starts_with("radroots local export ") => { - other.replacen("radroots local export ", "radroots store export ", 1) - } - other if other.starts_with("radroots local backup ") => { - other.replacen("radroots local backup ", "radroots store backup create ", 1) - } - other if other.starts_with("radroots market search ") => other.replacen( - "radroots market search ", - "radroots market product search ", - 1, - ), - other if other.starts_with("radroots market view ") => { - other.replacen("radroots market view ", "radroots market listing get ", 1) - } - other if other.starts_with("radroots order watch ") => { - other.replacen("radroots order watch ", "radroots order event watch ", 1) - } - other if other.starts_with("radroots order create --listing ") => other.replacen( - "radroots order create --listing ", - "radroots basket item add ", - 1, - ), - other if other.starts_with("radroots find ") => { - other.replacen("radroots find ", "radroots market product search ", 1) - } - other if other.starts_with("radroots sell add ") => { - other.replacen("radroots sell add ", "radroots listing create ", 1) - } - other if other.starts_with("radroots sell check ") => { - other.replacen("radroots sell check ", "radroots listing validate ", 1) - } - other if other.starts_with("radroots sell publish ") => { - other.replacen("radroots sell publish ", "radroots listing publish ", 1) - } - other if other.starts_with("radroots sell update ") => { - other.replacen("radroots sell update ", "radroots listing update ", 1) - } - other if other.starts_with("radroots sell pause ") => { - other.replacen("radroots sell pause ", "radroots listing archive ", 1) - } - other if other.starts_with("radroots sell show ") => { - other.replacen("radroots sell show ", "radroots listing get ", 1) - } - other => other.to_owned(), - } -} - macro_rules! target_operation_contracts { ($( $variant:ident => ($request:ident, $result:ident, $operation_id:literal) ),+ $(,)?) => { #[derive(Debug, Clone, PartialEq)] diff --git a/src/operation_basket.rs b/src/operation_basket.rs @@ -636,12 +636,7 @@ fn quote_actions(order: &OrderNewView) -> Vec<String> { vec![format!("radroots order submit {}", order.order_id)] } else { let mut actions = vec![format!("radroots order get {}", order.order_id)]; - actions.extend(order.actions.iter().map(|action| match action.as_str() { - "radroots account new" | "radroots account create" => { - "radroots account create".to_owned() - } - other => other.to_owned(), - })); + actions.extend(order.actions.iter().cloned()); actions } } diff --git a/src/operation_market.rs b/src/operation_market.rs @@ -98,11 +98,11 @@ fn market_refresh_view(mut view: SyncActionView) -> SyncActionView { actions.push("radroots relay list".to_owned()); } if actions.is_empty() { - actions.extend(target_actions(view.actions.as_slice())); + actions.extend(std::mem::take(&mut view.actions)); } actions } - _ => target_actions(view.actions.as_slice()), + _ => std::mem::take(&mut view.actions), }; view } @@ -132,7 +132,7 @@ fn market_product_search_view(mut view: FindView) -> FindView { "radroots store init".to_owned(), "radroots market refresh".to_owned(), ], - _ => target_actions(view.actions.as_slice()), + _ => std::mem::take(&mut view.actions), }; view } @@ -162,36 +162,11 @@ fn market_listing_get_view(mut view: ListingGetView) -> ListingGetView { "radroots store init".to_owned(), "radroots market refresh".to_owned(), ], - _ => target_actions(view.actions.as_slice()), + _ => std::mem::take(&mut view.actions), }; view } -fn target_actions(actions: &[String]) -> Vec<String> { - actions - .iter() - .map(|action| match action.as_str() { - "radroots local init" => "radroots store init".to_owned(), - "radroots sync status" => "radroots sync status get".to_owned(), - "radroots sync pull" => "radroots market refresh".to_owned(), - "radroots market update" => "radroots market refresh".to_owned(), - "radroots market search eggs" => "radroots market product search eggs".to_owned(), - "radroots market search tomatoes" => { - "radroots market product search tomatoes".to_owned() - } - other if other.starts_with("radroots market view ") => { - other.replacen("radroots market view ", "radroots market listing get ", 1) - } - other if other.starts_with("radroots order create --listing ") => other.replacen( - "radroots order create --listing ", - "radroots basket item add ", - 1, - ), - other => other.to_owned(), - }) - .collect() -} - fn listing_addr_can_back_basket(listing_addr: Option<&str>) -> bool { let Some(listing_addr) = listing_addr else { return false; diff --git a/src/operation_order.rs b/src/operation_order.rs @@ -122,10 +122,7 @@ where R: OperationResultData, T: Serialize, { - let mut value = serde_json::to_value(value) - .map_err(|error| OperationAdapterError::Serialization(error.to_string()))?; - translate_actions_in_value(&mut value); - OperationResult::new(R::from_value(value)) + OperationResult::new(R::from_serializable(value)?) } fn submit_result<R>( @@ -168,42 +165,6 @@ fn disposition_error( } } -fn translate_actions_in_value(value: &mut Value) { - match value { - Value::Object(object) => { - if let Some(Value::Array(actions)) = object.get_mut("actions") { - for action in actions { - if let Value::String(action) = action { - *action = target_action(action); - } - } - } - for nested in object.values_mut() { - translate_actions_in_value(nested); - } - } - Value::Array(values) => { - for nested in values { - translate_actions_in_value(nested); - } - } - _ => {} - } -} - -fn target_action(action: &str) -> String { - match action { - "radroots order ls" => "radroots order list".to_owned(), - "radroots order new" | "radroots order create" => "radroots basket create".to_owned(), - "radroots order history" => "radroots order event list".to_owned(), - "radroots rpc status" => "radroots runtime status get".to_owned(), - other if other.starts_with("radroots order watch ") => { - other.replacen("radroots order watch ", "radroots order event watch ", 1) - } - other => other.to_owned(), - } -} - fn required_order_key<P>(request: &OperationRequest<P>) -> Result<String, OperationAdapterError> where P: OperationRequestPayload + OperationRequestData, diff --git a/src/runtime/daemon.rs b/src/runtime/daemon.rs @@ -212,7 +212,7 @@ pub fn status(config: &RuntimeConfig) -> CommandOutput { actions: if status.ready { Vec::new() } else { - vec!["radroots relay ls".to_owned()] + vec!["radroots relay list".to_owned()] }, })), Err(DaemonRpcError::Unconfigured(reason)) diff --git a/src/runtime/farm.rs b/src/runtime/farm.rs @@ -82,7 +82,7 @@ pub fn set(config: &RuntimeConfig, args: &FarmSetArgs) -> Result<FarmSetView, Ru value: human_field_value(args.field, args.value.join(" ").trim()).to_owned(), config: None, reason: Some(format!("no farm draft found at {}", path.display())), - actions: vec!["radroots farm init".to_owned()], + actions: vec!["radroots farm create".to_owned()], }); }; @@ -107,7 +107,7 @@ pub fn set(config: &RuntimeConfig, args: &FarmSetArgs) -> Result<FarmSetView, Ru account_pubkey, )), reason: None, - actions: vec!["radroots farm check".to_owned()], + actions: vec!["radroots farm readiness check".to_owned()], }) } @@ -131,7 +131,7 @@ pub fn status( config: None, missing: vec!["Farm draft".to_owned()], reason: Some(format!("no farm config found at {}", path.display())), - actions: vec!["radroots farm init".to_owned()], + actions: vec!["radroots farm create".to_owned()], }); }; @@ -212,7 +212,7 @@ pub fn get(config: &RuntimeConfig, args: &FarmScopedArgs) -> Result<FarmGetView, config_present: false, document: None, reason: Some(format!("no farm config found at {}", path.display())), - actions: vec!["radroots farm init".to_owned()], + actions: vec!["radroots farm create".to_owned()], }); }; @@ -242,7 +242,7 @@ pub fn publish( args, format!("no farm config found at {}", path.display()), vec!["Farm draft".to_owned()], - vec!["radroots farm init".to_owned()], + vec!["radroots farm create".to_owned()], false, String::new(), String::new(), @@ -897,7 +897,7 @@ fn persist_publication_and_view( actions.push(format!("radroots job watch {job_id}")); } if actions.is_empty() { - actions.push("radroots rpc status".to_owned()); + actions.push("radroots runtime status get".to_owned()); } let reason = (state == "partial" || state == "unavailable" || state == "error") .then(|| "farm publish did not complete for both profile and farm record".to_owned()); @@ -1115,7 +1115,7 @@ fn save_draft_view( } fn farm_setup_actions(document: &FarmConfigDocument) -> Vec<String> { - let mut actions = vec!["radroots farm check".to_owned()]; + let mut actions = vec!["radroots farm readiness check".to_owned()]; if farm_config::missing_fields(document).is_empty() { actions.push("radroots farm publish".to_owned()); } diff --git a/src/runtime/find.rs b/src/runtime/find.rs @@ -59,7 +59,7 @@ pub fn search(config: &RuntimeConfig, args: &FindArgs) -> Result<FindView, Runti results: Vec::new(), hyf: None, reason: Some("local replica database is not initialized".to_owned()), - actions: vec!["radroots local init".to_owned()], + actions: vec!["radroots store init".to_owned()], }); } @@ -108,7 +108,7 @@ pub fn search(config: &RuntimeConfig, args: &FindArgs) -> Result<FindView, Runti let (state, reason, actions) = if results.is_empty() { let actions = if freshness.state == "never" { - vec!["radroots sync status".to_owned()] + vec!["radroots sync status get".to_owned()] } else { Vec::new() }; diff --git a/src/runtime/listing.rs b/src/runtime/listing.rs @@ -221,7 +221,7 @@ pub fn scaffold( output_path.display() )]; if defaults.selected_account_pubkey.is_none() { - actions.push("radroots account new".to_owned()); + actions.push("radroots account create".to_owned()); } if let Some(action) = &defaults.farm_next_action { actions.push(action.clone()); @@ -252,9 +252,15 @@ pub fn sell_add(config: &RuntimeConfig, args: &SellAddArgs) -> Result<SellAddVie write_listing_draft(&output_path, &draft, false)?; let summary = summarize_draft(&draft); - let mut actions = vec![format!("radroots sell check {}", output_path.display())]; + let mut actions = vec![format!( + "radroots listing validate {}", + output_path.display() + )]; if defaults.selected_account_pubkey.is_some() && defaults.selected_farm_d_tag.is_some() { - actions.push(format!("radroots sell publish {}", output_path.display())); + actions.push(format!( + "radroots listing publish {}", + output_path.display() + )); } if defaults.selected_account_pubkey.is_none() { actions.push("radroots account create".to_owned()); @@ -300,8 +306,8 @@ pub fn sell_show( location_primary: summary.location_primary, reason: None, actions: vec![ - format!("radroots sell check {}", args.file.display()), - format!("radroots sell publish {}", args.file.display()), + format!("radroots listing validate {}", args.file.display()), + format!("radroots listing publish {}", args.file.display()), ], }) } @@ -330,8 +336,8 @@ pub fn sell_reprice( .price .unwrap_or_else(|| args.price_expr.trim().to_owned()), actions: vec![ - format!("radroots sell check {}", args.file.display()), - format!("radroots sell update {}", args.file.display()), + format!("radroots listing validate {}", args.file.display()), + format!("radroots listing update {}", args.file.display()), ], }) } @@ -357,8 +363,8 @@ pub fn sell_restock( .stock .unwrap_or_else(|| format!("{} available", args.available.trim())), actions: vec![ - format!("radroots sell check {}", args.file.display()), - format!("radroots sell update {}", args.file.display()), + format!("radroots listing validate {}", args.file.display()), + format!("radroots listing update {}", args.file.display()), ], }) } @@ -372,10 +378,11 @@ pub fn sell_check( .ok() .map(|draft| summarize_draft(&draft)); let actions = if view.valid { - vec![format!("radroots sell publish {}", args.file.display())] + vec![format!("radroots listing publish {}", args.file.display())] } else { vec![ - format!("radroots sell show {}", args.file.display()), + format!("edit {}", args.file.display()), + format!("radroots listing validate {}", args.file.display()), "Edit the draft file and run the command again".to_owned(), ] }; @@ -586,35 +593,21 @@ fn read_listing_draft(path: &Path) -> Result<ListingDraftDocument, RuntimeError> }) } -fn translate_sell_actions(actions: &[String]) -> Vec<String> { - actions - .iter() - .map(|action| { - action - .replace("radroots listing validate ", "radroots sell check ") - .replace("radroots listing publish ", "radroots sell publish ") - .replace("radroots listing update ", "radroots sell update ") - .replace("radroots listing archive ", "radroots sell pause ") - .replace("radroots account new", "radroots account create") - }) - .collect() -} - fn successful_sell_mutation_actions(operation: &str, product_key: Option<&str>) -> Vec<String> { match operation { "publish" => { let mut actions = Vec::new(); if let Some(product_key) = product_key { - actions.push(format!("radroots market view {product_key}")); - actions.push(format!("radroots sell add {product_key}")); + actions.push(format!("radroots market listing get {product_key}")); + actions.push(format!("radroots listing create --key {product_key}")); } actions } "update" => product_key - .map(|product_key| vec![format!("radroots market view {product_key}")]) + .map(|product_key| vec![format!("radroots market listing get {product_key}")]) .unwrap_or_default(), "pause" => product_key - .map(|product_key| vec![format!("radroots sell add {product_key}")]) + .map(|product_key| vec![format!("radroots listing create --key {product_key}")]) .unwrap_or_default(), _ => Vec::new(), } @@ -648,7 +641,7 @@ fn sell_mutation_from_listing( "published" | "deduplicated" => { successful_sell_mutation_actions(operation, product_key.as_deref()) } - _ => translate_sell_actions(view.actions.as_slice()), + _ => view.actions, }; SellMutationView { @@ -971,7 +964,7 @@ pub fn get(config: &RuntimeConfig, args: &RecordKeyArgs) -> Result<ListingGetVie price: None, provenance, reason: Some("local replica database is not initialized".to_owned()), - actions: vec!["radroots local init".to_owned()], + actions: vec!["radroots store init".to_owned()], }); } @@ -998,7 +991,7 @@ pub fn get(config: &RuntimeConfig, args: &RecordKeyArgs) -> Result<ListingGetVie )), actions: vec![ "radroots sync pull".to_owned(), - format!("radroots find {}", args.key), + format!("radroots market product search {}", args.key), ], }); }; @@ -1197,7 +1190,7 @@ fn mutate( if let Some(job_id) = &Some(result.job_id.clone()) { actions.push(format!("radroots job get {job_id}")); } - actions.push("radroots rpc status".to_owned()); + actions.push("radroots runtime status get".to_owned()); } else { actions.push(format!("radroots job get {}", result.job_id)); actions.push(format!("radroots job watch {}", result.job_id)); @@ -1550,7 +1543,7 @@ fn invalid_validation_view( ) -> ListingValidateView { let mut actions = vec![format!("edit {}", file.display())]; if context.selected_account_id.is_none() { - actions.push("radroots account new".to_owned()); + actions.push("radroots account create".to_owned()); } if context.selected_farm_d_tag.is_none() { actions.push(context.farm_setup_action.clone()); @@ -2017,7 +2010,7 @@ fn authoring_defaults(config: &RuntimeConfig) -> Result<ListingAuthoringDefaults defaults.farm_next_action = None; defaults.farm_reason = None; } else { - defaults.farm_next_action = Some("radroots farm check".to_owned()); + defaults.farm_next_action = Some("radroots farm readiness check".to_owned()); defaults.farm_reason = Some( "selected farm draft is missing delivery or location defaults; those fields were left blank" .to_owned(), @@ -2048,7 +2041,7 @@ fn draft_location_from_model(location: &RadrootsListingLocation) -> ListingDraft } fn farm_setup_action(_config: &RuntimeConfig) -> Result<String, RuntimeError> { - Ok("radroots farm init".to_owned()) + Ok("radroots farm create".to_owned()) } fn configured_account( diff --git a/src/runtime/local.rs b/src/runtime/local.rs @@ -63,7 +63,7 @@ pub fn status(config: &RuntimeConfig) -> Result<LocalStatusView, RuntimeError> { pending_count: 0, }, reason: Some("local replica database is not initialized".to_owned()), - actions: vec!["radroots local init".to_owned()], + actions: vec!["radroots store init".to_owned()], }); } @@ -100,7 +100,7 @@ pub fn backup(config: &RuntimeConfig, output: &Path) -> Result<LocalBackupView, backup_format_version: String::new(), replica_db_version: String::new(), reason: Some("local replica database is not initialized".to_owned()), - actions: vec!["radroots local init".to_owned()], + actions: vec!["radroots store init".to_owned()], }); } @@ -140,7 +140,7 @@ pub fn export( export_version: String::new(), schema_hash: String::new(), reason: Some("local replica database is not initialized".to_owned()), - actions: vec!["radroots local init".to_owned()], + actions: vec!["radroots store init".to_owned()], }); } diff --git a/src/runtime/network.rs b/src/runtime/network.rs @@ -64,7 +64,7 @@ pub fn net_status(config: &RuntimeConfig) -> Result<NetStatusView, RuntimeError> fn relay_actions(config: &RuntimeConfig) -> Vec<String> { if config.relay.urls.is_empty() { - vec!["radroots relay ls --relay wss://relay.example.com".to_owned()] + vec!["radroots --relay wss://relay.example.com relay list".to_owned()] } else { Vec::new() } diff --git a/src/runtime/order.rs b/src/runtime/order.rs @@ -249,8 +249,8 @@ pub fn get(config: &RuntimeConfig, args: &RecordKeyArgs) -> Result<OrderGetView, reason: Some(format!("order draft `{lookup}` was not found")), issues: Vec::new(), actions: vec![ - "radroots order ls".to_owned(), - "radroots order new".to_owned(), + "radroots order list".to_owned(), + "radroots basket create".to_owned(), ], }); } @@ -288,7 +288,7 @@ pub fn list(config: &RuntimeConfig) -> Result<OrderListView, RuntimeError> { source: ORDER_SOURCE.to_owned(), count: 0, orders: Vec::new(), - actions: vec!["radroots order new".to_owned()], + actions: vec!["radroots basket create".to_owned()], }); } @@ -320,7 +320,7 @@ pub fn list(config: &RuntimeConfig) -> Result<OrderListView, RuntimeError> { "ready" }; let actions = if orders.is_empty() { - vec!["radroots order new".to_owned()] + vec!["radroots basket create".to_owned()] } else { Vec::new() }; @@ -360,8 +360,8 @@ pub fn submit( job: None, issues: Vec::new(), actions: vec![ - "radroots order ls".to_owned(), - "radroots order new".to_owned(), + "radroots order list".to_owned(), + "radroots basket create".to_owned(), ], }); } @@ -395,8 +395,11 @@ pub fn submit( if let Some(job) = submission_job_view(config, &loaded.document, true) { let mut actions = vec![ - format!("radroots order watch {}", loaded.document.order.order_id), - format!("radroots order history"), + format!( + "radroots order event watch {}", + loaded.document.order.order_id + ), + "radroots order event list".to_owned(), ]; actions.push(format!("radroots job get {}", job.job_id)); return Ok(OrderSubmitView { @@ -536,12 +539,15 @@ pub fn submit( let mut actions = Vec::new(); if failed { actions.push(format!("radroots job get {}", result.job_id)); - actions.push("radroots rpc status".to_owned()); - actions.push("radroots order history".to_owned()); + actions.push("radroots runtime status get".to_owned()); + actions.push("radroots order event list".to_owned()); } else { - actions.push(format!("radroots order watch {}", updated.order.order_id)); + actions.push(format!( + "radroots order event watch {}", + updated.order.order_id + )); actions.push(format!("radroots job get {}", result.job_id)); - actions.push("radroots order history".to_owned()); + actions.push("radroots order event list".to_owned()); } Ok(OrderSubmitView { @@ -609,7 +615,7 @@ pub fn watch( reason: Some(format!("order draft `{}` was not found", args.key)), workflow: None, frames: Vec::new(), - actions: vec!["radroots order ls".to_owned()], + actions: vec!["radroots order list".to_owned()], }); } @@ -685,7 +691,7 @@ pub fn watch( reason: Some(reason.clone()), workflow: Some(workflow), frames, - actions: vec!["radroots order history".to_owned()], + actions: vec!["radroots order event list".to_owned()], }); } } @@ -711,7 +717,7 @@ pub fn watch( reason: None, workflow, frames, - actions: vec!["radroots order history".to_owned()], + actions: vec!["radroots order event list".to_owned()], }); } } @@ -725,7 +731,7 @@ pub fn watch( reason: Some("recorded job id was not found in radrootsd".to_owned()), workflow: None, frames, - actions: vec!["radroots order history".to_owned()], + actions: vec!["radroots order event list".to_owned()], }); } Err(error) => return Ok(order_watch_error_view(&loaded, args, job_id, frames, error)), @@ -744,7 +750,7 @@ pub fn history(config: &RuntimeConfig) -> Result<OrderHistoryView, RuntimeError> count: 0, reason: Some("no submitted order drafts recorded yet".to_owned()), orders: Vec::new(), - actions: vec!["radroots order ls".to_owned()], + actions: vec!["radroots order list".to_owned()], }); } @@ -806,7 +812,7 @@ pub fn history(config: &RuntimeConfig) -> Result<OrderHistoryView, RuntimeError> reason, orders, actions: if state == "empty" { - vec!["radroots order new".to_owned()] + vec!["radroots basket create".to_owned()] } else { Vec::new() }, @@ -826,7 +832,7 @@ pub fn cancel( order_id: None, reason: Some(format!("order draft `{}` was not found", args.key)), job: None, - actions: vec!["radroots order ls".to_owned()], + actions: vec!["radroots order list".to_owned()], }); } @@ -875,7 +881,7 @@ pub fn cancel( ), job: Some(job), actions: vec![ - "radroots order history".to_owned(), + "radroots order event list".to_owned(), format!("radroots job get {}", job_id.unwrap_or_default()), ], }) @@ -914,7 +920,7 @@ fn resolve_order_listing( if !config.local.replica_db_path.exists() { return Err(RuntimeError::Config(format!( - "order listing lookup `{listing_lookup}` requires local market data; run `radroots local init` and `radroots market update` before creating an order from a listing" + "order listing lookup `{listing_lookup}` requires local market data; run `radroots store init` and `radroots market refresh` before creating an order from a listing" ))); } @@ -922,23 +928,23 @@ fn resolve_order_listing( let rows = db.trade_product_lookup(listing_lookup)?; match rows.len() { 0 => Err(RuntimeError::Config(format!( - "listing `{listing_lookup}` is not available in the local replica; run `radroots market update` or pass `--listing-addr`" + "listing `{listing_lookup}` is not available in the local replica; run `radroots market refresh` or pass `--listing-addr`" ))), 1 => { let row = rows.into_iter().next().expect("one row"); let listing_addr = normalize_optional(row.listing_addr.as_deref()).ok_or_else(|| { RuntimeError::Config(format!( - "listing `{listing_lookup}` is missing a canonical listing address; run `radroots market update` or pass `--listing-addr`" + "listing `{listing_lookup}` is missing a canonical listing address; run `radroots market refresh` or pass `--listing-addr`" )) })?; let parsed = parse_listing_addr(listing_addr.as_str()).map_err(|error| { RuntimeError::Config(format!( - "listing `{listing_lookup}` has invalid listing_addr: {error}; run `radroots market update` or pass `--listing-addr`" + "listing `{listing_lookup}` has invalid listing_addr: {error}; run `radroots market refresh` or pass `--listing-addr`" )) })?; if parsed.kind != KIND_LISTING { return Err(RuntimeError::Config(format!( - "listing `{listing_lookup}` listing_addr must reference a public NIP-99 listing; run `radroots market update` or pass `--listing-addr`" + "listing `{listing_lookup}` listing_addr must reference a public NIP-99 listing; run `radroots market refresh` or pass `--listing-addr`" ))); } @@ -975,7 +981,7 @@ fn view_from_loaded( actions_for_document(&loaded.document, loaded.file.as_path(), issues.as_slice()); if let Some(job) = &job { actions.push(format!("radroots job get {}", job.job_id)); - actions.push("radroots order history".to_owned()); + actions.push("radroots order event list".to_owned()); } OrderGetView { @@ -1217,7 +1223,7 @@ fn actions_for_document( file.display() )); if document.buyer_account_id.is_none() && document.order.buyer_pubkey.trim().is_empty() { - actions.push("radroots account new".to_owned()); + actions.push("radroots account create".to_owned()); } if document.order.items.is_empty() || issues diff --git a/src/runtime/sync.rs b/src/runtime/sync.rs @@ -14,7 +14,7 @@ use crate::runtime::RuntimeError; use crate::runtime::config::RuntimeConfig; const SYNC_SOURCE: &str = "local replica ยท local first"; -const RELAY_SETUP_ACTION: &str = "radroots relay ls --relay wss://relay.example.com"; +const RELAY_SETUP_ACTION: &str = "radroots --relay wss://relay.example.com relay list"; #[derive(Debug, Clone)] struct SyncSnapshot { @@ -122,7 +122,7 @@ fn narrowed_action( }); } - let mut actions = vec!["radroots sync status".to_owned()]; + let mut actions = vec!["radroots sync status get".to_owned()]; actions.extend(snapshot.actions); Ok(SyncActionView { @@ -160,7 +160,7 @@ fn inspect_sync(config: &RuntimeConfig) -> Result<SyncSnapshot, RuntimeError> { pending_count: 0, }, reason: Some("local replica database is not initialized".to_owned()), - actions: vec!["radroots local init".to_owned()], + actions: vec!["radroots store init".to_owned()], }); } diff --git a/src/runtime/workflow.rs b/src/runtime/workflow.rs @@ -52,7 +52,7 @@ pub fn setup(config: &RuntimeConfig, role: SetupRoleArg) -> Result<SetupView, Ru match role { SetupRoleArg::Buyer | SetupRoleArg::Both if relay_configured => { - push_next(&mut next, Some("radroots market search tomatoes")); + push_next(&mut next, Some("radroots market product search tomatoes")); } _ => {} } @@ -61,7 +61,7 @@ pub fn setup(config: &RuntimeConfig, role: SetupRoleArg) -> Result<SetupView, Ru push_next(&mut next, Some(RELAY_SETUP_ACTION)); } - push_next(&mut next, Some("radroots status")); + push_next(&mut next, Some("radroots health status get")); } Ok(SetupView { @@ -119,21 +119,23 @@ pub fn status(config: &RuntimeConfig) -> Result<StatusView, RuntimeError> { if relay_configured { match farm.state { - "draft" | "published" => push_next(&mut next, Some("radroots sell add tomatoes")), - "missing" => push_next(&mut next, Some("radroots market search tomatoes")), + "draft" | "published" => { + push_next(&mut next, Some("radroots listing create --key tomatoes")) + } + "missing" => push_next(&mut next, Some("radroots market product search tomatoes")), _ => {} } } } else if account_resolution.resolved_account.is_some() { - push_next(&mut next, Some("radroots setup buyer")); - push_next(&mut next, Some("radroots setup seller")); + push_next(&mut next, Some("radroots basket create")); + push_next(&mut next, Some("radroots farm create")); if account_resolution.resolved_account.is_some() && local_status.state == "ready" && !relay_configured { next.clear(); push_next(&mut next, Some(RELAY_SETUP_ACTION)); - push_next(&mut next, Some("radroots status")); + push_next(&mut next, Some("radroots health status get")); } } @@ -276,9 +278,9 @@ fn unresolved_account_resolution_state( fn setup_command(role: SetupRoleArg) -> &'static str { match role { - SetupRoleArg::Seller => "radroots setup seller", - SetupRoleArg::Buyer => "radroots setup buyer", - SetupRoleArg::Both => "radroots setup both", + SetupRoleArg::Seller => "radroots farm create", + SetupRoleArg::Buyer => "radroots basket create", + SetupRoleArg::Both => "radroots workspace init", } } diff --git a/tests/signer_runtime_modes.rs b/tests/signer_runtime_modes.rs @@ -5,8 +5,9 @@ use std::path::Path; use radroots_events::kinds::KIND_LISTING; use serde_json::json; use support::{ - RadrootsCliSandbox, assert_contains, assert_hex_len, create_listing_draft, identity_public, - make_listing_publishable, shell_single_quoted, toml_string, write_public_identity_profile, + RadrootsCliSandbox, assert_contains, assert_hex_len, assert_no_removed_command_reference, + create_listing_draft, identity_public, make_listing_publishable, shell_single_quoted, + toml_string, write_public_identity_profile, }; #[test] @@ -236,6 +237,7 @@ fn myc_signer_status_returns_deferred_signer_error() { assert_eq!(value["errors"][0]["exit_code"], 7); assert_eq!(value["errors"][0]["detail"]["class"], "signer"); assert_contains(&value["errors"][0]["message"], "signer mode `myc`"); + assert_no_removed_command_reference(&value, &["signer", "status", "get"]); } #[cfg(unix)] @@ -307,6 +309,7 @@ fn local_listing_publish_dry_run_validates_local_account_authority() { assert_eq!(value["result"], serde_json::Value::Null); assert_eq!(value["errors"][0]["code"], "account_unresolved"); assert_eq!(value["errors"][0]["detail"]["class"], "account"); + assert_no_removed_command_reference(&value, &["listing", "publish", "--dry-run"]); } #[test] @@ -360,6 +363,7 @@ fn local_listing_publish_signs_with_selected_account_without_remote_fallback() { .is_some_and(|items| items.first() == Some(&json!("d")) && items.get(1) == Some(&value["result"]["listing_id"]))) ); + assert_no_removed_command_reference(&value, &["listing", "publish"]); } #[test] @@ -383,6 +387,7 @@ fn local_listing_publish_dry_run_does_not_sign_matching_listing() { assert_eq!(value["result"]["state"], "dry_run"); assert_eq!(value["result"]["dry_run"], true); assert_eq!(value["result"]["event_id"], serde_json::Value::Null); + assert_no_removed_command_reference(&value, &["listing", "publish", "--dry-run"]); } #[test] @@ -422,6 +427,7 @@ fn local_listing_publish_fails_when_selected_account_does_not_match_seller() { &value["errors"][0]["message"], "cannot sign listing seller_pubkey", ); + assert_no_removed_command_reference(&value, &["listing", "publish", "account mismatch"]); } #[test] diff --git a/tests/support/mod.rs b/tests/support/mod.rs @@ -115,6 +115,20 @@ pub fn assert_no_removed_command_reference(value: &Value, args: &[&str]) { "radroots net", "radroots myc", "radroots rpc", + "radroots account new", + "radroots config show", + "radroots runtime config show", + "radroots market search", + "radroots market view", + "radroots market update", + "radroots order ls", + "radroots order history", + "radroots order watch", + "radroots order new", + "radroots order create", + "radroots farm init", + "radroots farm check", + "radroots relay ls", "radroots product", "radroots message", "radroots approval", diff --git a/tests/target_cli.rs b/tests/target_cli.rs @@ -227,6 +227,10 @@ fn listing_publish_dry_run_validates_missing_file() { assert_eq!(value["operation_id"], "listing.publish"); assert_eq!(value["result"], Value::Null); assert_eq!(value["errors"][0]["code"], "runtime_error"); + assert_no_removed_command_reference( + &value, + &["listing", "publish", "--dry-run", "missing-listing.toml"], + ); } #[test] @@ -281,10 +285,12 @@ fn buyer_target_flow_acceptance_uses_target_operations() { let search = sandbox.json_success(&["--format", "json", "market", "product", "search", "eggs"]); assert_eq!(search["operation_id"], "market.product.search"); assert_eq!(search["errors"].as_array().expect("errors").len(), 0); + assert_no_removed_command_reference(&search, &["market", "product", "search"]); let create = sandbox.json_success(&["--format", "json", "basket", "create", "basket_flow"]); assert_eq!(create["operation_id"], "basket.create"); assert_eq!(create["result"]["basket_id"], "basket_flow"); + assert_no_removed_command_reference(&create, &["basket", "create"]); let add = sandbox.json_success(&[ "--format", @@ -302,6 +308,7 @@ fn buyer_target_flow_acceptance_uses_target_operations() { ]); assert_eq!(add["operation_id"], "basket.item.add"); assert_eq!(add["result"]["ready_for_quote"], true); + assert_no_removed_command_reference(&add, &["basket", "item", "add"]); let quote = sandbox.json_success(&[ "--format", @@ -313,6 +320,7 @@ fn buyer_target_flow_acceptance_uses_target_operations() { ]); assert_eq!(quote["operation_id"], "basket.quote.create"); assert_eq!(quote["result"]["state"], "quoted"); + assert_no_removed_command_reference(&quote, &["basket", "quote", "create"]); let order_id = quote["result"]["quote"]["order_id"] .as_str() .expect("order id"); @@ -327,6 +335,7 @@ fn buyer_target_flow_acceptance_uses_target_operations() { orders["result"]["orders"][0]["issues"][0]["field"], "buyer_account_id" ); + assert_no_removed_command_reference(&orders, &["order", "list"]); let submit = sandbox.json_success(&["--format", "json", "--dry-run", "order", "submit", order_id]); @@ -342,6 +351,7 @@ fn buyer_target_flow_acceptance_uses_target_operations() { .contains("not ready for durable submit") ); assert_eq!(submit["errors"].as_array().expect("errors").len(), 0); + assert_no_removed_command_reference(&submit, &["order", "submit", "--dry-run"]); } #[test] @@ -353,6 +363,7 @@ fn seller_target_flow_acceptance_uses_target_operations() { let account = sandbox.json_success(&["--format", "json", "account", "create"]); assert_eq!(account["operation_id"], "account.create"); assert_eq!(account["result"]["account"]["signer"], "local"); + assert_no_removed_command_reference(&account, &["account", "create"]); let farm = sandbox.json_success(&[ "--format", @@ -368,6 +379,7 @@ fn seller_target_flow_acceptance_uses_target_operations() { ]); assert_eq!(farm["operation_id"], "farm.create"); assert_eq!(farm["result"]["state"], "saved"); + assert_no_removed_command_reference(&farm, &["farm", "create"]); let create = sandbox.json_success(&[ "--format", @@ -403,6 +415,7 @@ fn seller_target_flow_acceptance_uses_target_operations() { ]); assert_eq!(create["operation_id"], "listing.create"); assert_eq!(create["result"]["file"], listing_file); + assert_no_removed_command_reference(&create, &["listing", "create"]); let validate = sandbox.json_success(&[ "--format", @@ -414,6 +427,7 @@ fn seller_target_flow_acceptance_uses_target_operations() { assert_eq!(validate["operation_id"], "listing.validate"); assert_eq!(validate["result"]["valid"], true); assert_eq!(validate["result"]["issues"], Value::Null); + assert_no_removed_command_reference(&validate, &["listing", "validate"]); let publish = sandbox.json_success(&[ "--format", @@ -425,6 +439,7 @@ fn seller_target_flow_acceptance_uses_target_operations() { ]); assert_eq!(publish["operation_id"], "listing.publish"); assert_eq!(publish["result"]["state"], "dry_run"); + assert_no_removed_command_reference(&publish, &["listing", "publish", "--dry-run"]); let signed = sandbox.json_success(&[ "--format", @@ -443,4 +458,5 @@ fn seller_target_flow_acceptance_uses_target_operations() { signed["result"]["seller_pubkey"] ); assert!(signed["result"]["event"]["signature"].is_string()); + assert_no_removed_command_reference(&signed, &["listing", "publish"]); }