From 9662a054b2ca442cab7d114bdc011b4fd04c648d Mon Sep 17 00:00:00 2001 From: Otto Date: Thu, 26 Mar 2026 18:00:55 +0000 Subject: [PATCH 1/3] feat: add CLI subcommands for recovery agent management MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add `walletkit recovery-agent` subcommand group with three actions: - `initiate ` — initiate a time-locked update - `execute` — execute a pending update after cooldown - `cancel` — cancel a pending update Each delegates to the corresponding Authenticator method and prints the returned gateway request ID. --- walletkit-cli/src/commands/mod.rs | 9 +++ walletkit-cli/src/commands/recovery_agent.rs | 75 ++++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 walletkit-cli/src/commands/recovery_agent.rs diff --git a/walletkit-cli/src/commands/mod.rs b/walletkit-cli/src/commands/mod.rs index dcbb2ef7..9e2ef2bd 100644 --- a/walletkit-cli/src/commands/mod.rs +++ b/walletkit-cli/src/commands/mod.rs @@ -3,6 +3,7 @@ mod auth; mod credential; mod proof; +mod recovery_agent; mod wallet; use std::path::PathBuf; @@ -97,6 +98,11 @@ pub enum Command { #[command(subcommand)] action: proof::ProofCommand, }, + /// Recovery agent management (initiate, execute, cancel updates). + RecoveryAgent { + #[command(subcommand)] + action: recovery_agent::RecoveryAgentCommand, + }, } /// Resolves the wallet root directory, defaulting to `~/.walletkit`. @@ -232,5 +238,8 @@ pub async fn run(cli: Cli) -> eyre::Result<()> { Command::Auth { action } => auth::run(&cli, action).await, Command::Credential { action } => credential::run(&cli, action).await, Command::Proof { action } => proof::run(&cli, action).await, + Command::RecoveryAgent { action } => { + recovery_agent::run(&cli, action).await + } } } diff --git a/walletkit-cli/src/commands/recovery_agent.rs b/walletkit-cli/src/commands/recovery_agent.rs new file mode 100644 index 00000000..f8354133 --- /dev/null +++ b/walletkit-cli/src/commands/recovery_agent.rs @@ -0,0 +1,75 @@ +//! `walletkit recovery-agent` subcommands — recovery agent management. + +use clap::Subcommand; +use eyre::WrapErr as _; + +use crate::output; + +use super::{init_authenticator, Cli}; + +#[derive(Subcommand)] +pub enum RecoveryAgentCommand { + /// Initiate a time-locked recovery agent update (14-day cooldown). + InitiateUpdate { + /// Checksummed hex address of the new recovery agent (e.g. "0x1234…"). + new_recovery_agent: String, + }, + /// Execute a pending recovery agent update after the cooldown has elapsed. + ExecuteUpdate, + /// Cancel a pending recovery agent update before the cooldown expires. + CancelUpdate, +} + +pub async fn run(cli: &Cli, action: &RecoveryAgentCommand) -> eyre::Result<()> { + let (authenticator, _store) = init_authenticator(cli).await?; + + match action { + RecoveryAgentCommand::InitiateUpdate { + new_recovery_agent, + } => { + let request_id = authenticator + .initiate_recovery_agent_update(new_recovery_agent.clone()) + .await + .wrap_err("initiate recovery agent update failed")?; + + let data = serde_json::json!({ "request_id": request_id }); + if cli.json { + output::print_json_data(&data, true); + } else { + println!("Recovery agent update initiated. Request ID: {request_id}"); + } + } + RecoveryAgentCommand::ExecuteUpdate => { + let request_id = authenticator + .execute_recovery_agent_update() + .await + .wrap_err("execute recovery agent update failed")?; + + let data = serde_json::json!({ "request_id": request_id }); + if cli.json { + output::print_json_data(&data, true); + } else { + println!( + "Recovery agent update executed. Request ID: {request_id}" + ); + } + } + RecoveryAgentCommand::CancelUpdate => { + let request_id = authenticator + .cancel_recovery_agent_update() + .await + .wrap_err("cancel recovery agent update failed")?; + + let data = serde_json::json!({ "request_id": request_id }); + if cli.json { + output::print_json_data(&data, true); + } else { + println!( + "Recovery agent update cancelled. Request ID: {request_id}" + ); + } + } + } + + Ok(()) +} From 1c042e723ea6cf54ca0f54cb0646f24ce5be1d80 Mon Sep 17 00:00:00 2001 From: Otto Date: Thu, 26 Mar 2026 18:14:53 +0000 Subject: [PATCH 2/3] fix: repair recovery-agent CLI lint failures --- walletkit-cli/src/commands/mod.rs | 4 +--- walletkit-cli/src/commands/recovery_agent.rs | 22 +++++++------------- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/walletkit-cli/src/commands/mod.rs b/walletkit-cli/src/commands/mod.rs index 9e2ef2bd..31f221b3 100644 --- a/walletkit-cli/src/commands/mod.rs +++ b/walletkit-cli/src/commands/mod.rs @@ -238,8 +238,6 @@ pub async fn run(cli: Cli) -> eyre::Result<()> { Command::Auth { action } => auth::run(&cli, action).await, Command::Credential { action } => credential::run(&cli, action).await, Command::Proof { action } => proof::run(&cli, action).await, - Command::RecoveryAgent { action } => { - recovery_agent::run(&cli, action).await - } + Command::RecoveryAgent { action } => recovery_agent::run(&cli, action).await, } } diff --git a/walletkit-cli/src/commands/recovery_agent.rs b/walletkit-cli/src/commands/recovery_agent.rs index f8354133..05b74d71 100644 --- a/walletkit-cli/src/commands/recovery_agent.rs +++ b/walletkit-cli/src/commands/recovery_agent.rs @@ -10,23 +10,21 @@ use super::{init_authenticator, Cli}; #[derive(Subcommand)] pub enum RecoveryAgentCommand { /// Initiate a time-locked recovery agent update (14-day cooldown). - InitiateUpdate { + Initiate { /// Checksummed hex address of the new recovery agent (e.g. "0x1234…"). new_recovery_agent: String, }, /// Execute a pending recovery agent update after the cooldown has elapsed. - ExecuteUpdate, + Execute, /// Cancel a pending recovery agent update before the cooldown expires. - CancelUpdate, + Cancel, } pub async fn run(cli: &Cli, action: &RecoveryAgentCommand) -> eyre::Result<()> { let (authenticator, _store) = init_authenticator(cli).await?; match action { - RecoveryAgentCommand::InitiateUpdate { - new_recovery_agent, - } => { + RecoveryAgentCommand::Initiate { new_recovery_agent } => { let request_id = authenticator .initiate_recovery_agent_update(new_recovery_agent.clone()) .await @@ -39,7 +37,7 @@ pub async fn run(cli: &Cli, action: &RecoveryAgentCommand) -> eyre::Result<()> { println!("Recovery agent update initiated. Request ID: {request_id}"); } } - RecoveryAgentCommand::ExecuteUpdate => { + RecoveryAgentCommand::Execute => { let request_id = authenticator .execute_recovery_agent_update() .await @@ -49,12 +47,10 @@ pub async fn run(cli: &Cli, action: &RecoveryAgentCommand) -> eyre::Result<()> { if cli.json { output::print_json_data(&data, true); } else { - println!( - "Recovery agent update executed. Request ID: {request_id}" - ); + println!("Recovery agent update executed. Request ID: {request_id}"); } } - RecoveryAgentCommand::CancelUpdate => { + RecoveryAgentCommand::Cancel => { let request_id = authenticator .cancel_recovery_agent_update() .await @@ -64,9 +60,7 @@ pub async fn run(cli: &Cli, action: &RecoveryAgentCommand) -> eyre::Result<()> { if cli.json { output::print_json_data(&data, true); } else { - println!( - "Recovery agent update cancelled. Request ID: {request_id}" - ); + println!("Recovery agent update cancelled. Request ID: {request_id}"); } } } From e77d774d9b4084c9787dbbc3055c5ef06d6243f2 Mon Sep 17 00:00:00 2001 From: Otto Date: Thu, 26 Mar 2026 18:27:32 +0000 Subject: [PATCH 3/3] fix: rename recovery-agent CLI command --- walletkit-cli/src/commands/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/walletkit-cli/src/commands/mod.rs b/walletkit-cli/src/commands/mod.rs index 31f221b3..81c7461b 100644 --- a/walletkit-cli/src/commands/mod.rs +++ b/walletkit-cli/src/commands/mod.rs @@ -99,6 +99,7 @@ pub enum Command { action: proof::ProofCommand, }, /// Recovery agent management (initiate, execute, cancel updates). + #[command(name = "recovery-agent-update")] RecoveryAgent { #[command(subcommand)] action: recovery_agent::RecoveryAgentCommand,