mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-09-07 08:27:12 +00:00
feat(cli): add fabro archive and fabro unarchive commands
Two new top-level commands mirror `fabro rm`'s bulk-by-ID shape: positional run identifiers, per-ID success/error aggregation, and a final non-zero exit if any item failed. Calls the new server endpoints from Unit 5. Shared bulk loop covers both directions and emits structured JSON with an `archived` or `unarchived` list alongside `errors`. Top-level help snapshot updated.
This commit is contained in:
parent
03fc375cdd
commit
8bf243fe47
5 changed files with 201 additions and 0 deletions
|
|
@ -292,6 +292,26 @@ pub(crate) struct RunsRemoveArgs {
|
|||
pub(crate) force: bool,
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
pub(crate) struct RunsArchiveArgs {
|
||||
#[command(flatten)]
|
||||
pub(crate) server: ServerTargetArgs,
|
||||
|
||||
/// Run IDs or workflow names to archive
|
||||
#[arg(required = true)]
|
||||
pub(crate) runs: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
pub(crate) struct RunsUnarchiveArgs {
|
||||
#[command(flatten)]
|
||||
pub(crate) server: ServerTargetArgs,
|
||||
|
||||
/// Run IDs or workflow names to unarchive
|
||||
#[arg(required = true)]
|
||||
pub(crate) runs: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
pub(crate) struct LogsArgs {
|
||||
#[command(flatten)]
|
||||
|
|
@ -942,6 +962,11 @@ pub(crate) enum RunsCommands {
|
|||
Rm(RunsRemoveArgs),
|
||||
/// Show detailed information about a workflow run
|
||||
Inspect(InspectArgs),
|
||||
/// Mark terminal runs as archived (reviewed, no further action needed).
|
||||
/// Archived runs are hidden from default listings.
|
||||
Archive(RunsArchiveArgs),
|
||||
/// Restore archived runs to their prior terminal status.
|
||||
Unarchive(RunsUnarchiveArgs),
|
||||
}
|
||||
|
||||
impl RunsCommands {
|
||||
|
|
@ -950,6 +975,8 @@ impl RunsCommands {
|
|||
Self::Ps(_) => "ps",
|
||||
Self::Rm(_) => "rm",
|
||||
Self::Inspect(_) => "inspect",
|
||||
Self::Archive(_) => "archive",
|
||||
Self::Unarchive(_) => "unarchive",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
145
lib/crates/fabro-cli/src/commands/runs/archive.rs
Normal file
145
lib/crates/fabro-cli/src/commands/runs/archive.rs
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
use anyhow::{Result, bail};
|
||||
use fabro_types::settings::CliSettings;
|
||||
use fabro_types::settings::cli::{CliLayer, OutputFormat};
|
||||
use fabro_util::printer::Printer;
|
||||
|
||||
use super::short_run_id;
|
||||
use crate::args::{RunsArchiveArgs, RunsUnarchiveArgs};
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::server_client;
|
||||
use crate::server_runs::{
|
||||
ServerRunSummaryInfo, ServerSummaryLookup, resolve_server_run_from_summaries,
|
||||
};
|
||||
use crate::shared::print_json_pretty;
|
||||
|
||||
pub(crate) async fn archive_command(
|
||||
args: &RunsArchiveArgs,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
let ctx = CommandContext::for_target(&args.server, printer, cli.clone(), cli_layer)?;
|
||||
let lookup = ServerSummaryLookup::from_client(ctx.server().await?).await?;
|
||||
run_bulk(
|
||||
Action::Archive,
|
||||
&args.runs,
|
||||
lookup.client(),
|
||||
lookup.runs(),
|
||||
cli,
|
||||
printer,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub(crate) async fn unarchive_command(
|
||||
args: &RunsUnarchiveArgs,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
let ctx = CommandContext::for_target(&args.server, printer, cli.clone(), cli_layer)?;
|
||||
let lookup = ServerSummaryLookup::from_client(ctx.server().await?).await?;
|
||||
run_bulk(
|
||||
Action::Unarchive,
|
||||
&args.runs,
|
||||
lookup.client(),
|
||||
lookup.runs(),
|
||||
cli,
|
||||
printer,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum Action {
|
||||
Archive,
|
||||
Unarchive,
|
||||
}
|
||||
|
||||
impl Action {
|
||||
fn verb_ing(self) -> &'static str {
|
||||
match self {
|
||||
Self::Archive => "archive",
|
||||
Self::Unarchive => "unarchive",
|
||||
}
|
||||
}
|
||||
|
||||
fn past(self) -> &'static str {
|
||||
match self {
|
||||
Self::Archive => "archived",
|
||||
Self::Unarchive => "unarchived",
|
||||
}
|
||||
}
|
||||
|
||||
fn json_key(self) -> &'static str {
|
||||
self.past()
|
||||
}
|
||||
}
|
||||
|
||||
async fn run_bulk(
|
||||
action: Action,
|
||||
identifiers: &[String],
|
||||
client: &server_client::ServerStoreClient,
|
||||
runs: &[ServerRunSummaryInfo],
|
||||
cli: &CliSettings,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
let json = cli.output.format == OutputFormat::Json;
|
||||
let mut had_errors = false;
|
||||
let mut changed = Vec::new();
|
||||
let mut errors = Vec::new();
|
||||
|
||||
for identifier in identifiers {
|
||||
let run = match resolve_server_run_from_summaries(runs, identifier) {
|
||||
Ok(run) => run,
|
||||
Err(err) => {
|
||||
if !json {
|
||||
fabro_util::printerr!(printer, "error: {identifier}: {err}");
|
||||
}
|
||||
errors.push(serde_json::json!({
|
||||
"identifier": identifier,
|
||||
"error": err.to_string(),
|
||||
}));
|
||||
had_errors = true;
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let run_id = run.run_id();
|
||||
let result = match action {
|
||||
Action::Archive => client.archive_run(&run_id).await,
|
||||
Action::Unarchive => client.unarchive_run(&run_id).await,
|
||||
};
|
||||
match result {
|
||||
Ok(()) => {
|
||||
let run_id_string = run_id.to_string();
|
||||
changed.push(run_id_string.clone());
|
||||
if !json {
|
||||
fabro_util::printerr!(printer, "{}", short_run_id(&run_id_string));
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
if !json {
|
||||
fabro_util::printerr!(printer, "error: {identifier}: {err}");
|
||||
}
|
||||
errors.push(serde_json::json!({
|
||||
"identifier": identifier,
|
||||
"error": err.to_string(),
|
||||
}));
|
||||
had_errors = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if json {
|
||||
let mut body = serde_json::Map::new();
|
||||
body.insert(action.json_key().to_string(), serde_json::json!(changed));
|
||||
body.insert("errors".to_string(), serde_json::json!(errors));
|
||||
print_json_pretty(&serde_json::Value::Object(body))?;
|
||||
}
|
||||
|
||||
if had_errors {
|
||||
bail!("some runs could not be {}", action.verb_ing());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ use fabro_util::terminal::Styles;
|
|||
|
||||
use crate::args::RunsCommands;
|
||||
|
||||
pub(crate) mod archive;
|
||||
pub(crate) mod inspect;
|
||||
pub(crate) mod list;
|
||||
pub(crate) mod rm;
|
||||
|
|
@ -23,6 +24,12 @@ pub(crate) async fn dispatch(
|
|||
}
|
||||
RunsCommands::Rm(args) => rm::remove_command(&args, cli, cli_layer, printer).await,
|
||||
RunsCommands::Inspect(args) => inspect::run(&args, cli, cli_layer, printer).await,
|
||||
RunsCommands::Archive(args) => {
|
||||
archive::archive_command(&args, cli, cli_layer, printer).await
|
||||
}
|
||||
RunsCommands::Unarchive(args) => {
|
||||
archive::unarchive_command(&args, cli, cli_layer, printer).await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -594,6 +594,26 @@ impl ServerStoreClient {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn archive_run(&self, run_id: &RunId) -> Result<()> {
|
||||
self.client
|
||||
.archive_run()
|
||||
.id(run_id.to_string())
|
||||
.send()
|
||||
.await
|
||||
.map_err(map_api_error)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn unarchive_run(&self, run_id: &RunId) -> Result<()> {
|
||||
self.client
|
||||
.unarchive_run()
|
||||
.id(run_id.to_string())
|
||||
.send()
|
||||
.await
|
||||
.map_err(map_api_error)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn list_store_runs(&self) -> Result<Vec<RunSummary>> {
|
||||
let mut all_runs = Vec::new();
|
||||
let mut offset = 0_u64;
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ fn help() {
|
|||
store Export store-backed run state for debugging
|
||||
rm Remove one or more workflow runs
|
||||
inspect Show detailed information about a workflow run
|
||||
archive Mark terminal runs as archived (reviewed, no further action needed). Archived runs are hidden from default listings
|
||||
unarchive Restore archived runs to their prior terminal status
|
||||
model List and test LLM models
|
||||
server Server operations
|
||||
doctor Check environment and integration health
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue