mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-10-07 03:00:29 +00:00
Merge remote-tracking branch 'origin/main'
This commit is contained in:
commit
2047e490d8
61 changed files with 849 additions and 1230 deletions
|
|
@ -3,6 +3,10 @@ use std::path::{Path, PathBuf};
|
|||
|
||||
use clap::{Args, Subcommand, ValueEnum};
|
||||
use fabro_agent::cli::AgentArgs;
|
||||
use fabro_types::settings::cli::{
|
||||
CliLayer, CliLoggingLayer, CliOutputLayer, CliUpdatesLayer, OutputFormat, OutputVerbosity,
|
||||
};
|
||||
use fabro_util::printer::Printer;
|
||||
|
||||
pub(crate) const LONG_VERSION: &str = concat!(
|
||||
env!("CARGO_PKG_VERSION"),
|
||||
|
|
@ -36,13 +40,46 @@ pub(crate) struct GlobalArgs {
|
|||
pub verbose: bool,
|
||||
}
|
||||
|
||||
impl GlobalArgs {
|
||||
pub(crate) fn require_no_json(&self) -> anyhow::Result<()> {
|
||||
anyhow::ensure!(!self.json, "--json is not supported for this command");
|
||||
Ok(())
|
||||
pub(crate) fn global_args_cli_layer(globals: &GlobalArgs) -> CliLayer {
|
||||
let format = globals.json.then_some(OutputFormat::Json);
|
||||
let verbosity = match (globals.quiet, globals.verbose) {
|
||||
(true, _) => Some(OutputVerbosity::Quiet),
|
||||
(_, true) => Some(OutputVerbosity::Verbose),
|
||||
_ => None,
|
||||
};
|
||||
let output =
|
||||
(format.is_some() || verbosity.is_some()).then_some(CliOutputLayer { format, verbosity });
|
||||
let updates = globals
|
||||
.no_upgrade_check
|
||||
.then_some(CliUpdatesLayer { check: Some(false) });
|
||||
let logging = globals.debug.then_some(CliLoggingLayer {
|
||||
level: Some("debug".to_string()),
|
||||
});
|
||||
|
||||
CliLayer {
|
||||
output,
|
||||
updates,
|
||||
logging,
|
||||
..CliLayer::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn printer_from_verbosity(verbosity: OutputVerbosity) -> Printer {
|
||||
match verbosity {
|
||||
OutputVerbosity::Quiet => Printer::Quiet,
|
||||
OutputVerbosity::Normal => Printer::Default,
|
||||
OutputVerbosity::Verbose => Printer::Verbose,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn require_no_json_override(process_local_json: bool) -> anyhow::Result<()> {
|
||||
anyhow::ensure!(
|
||||
!process_local_json,
|
||||
"--json is not supported for this command"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Args, Debug, Clone, Default)]
|
||||
pub(crate) struct StorageDirArgs {
|
||||
/// Local storage directory (default: ~/.fabro/storage)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ use std::path::{Path, PathBuf};
|
|||
use std::sync::Arc;
|
||||
|
||||
use anyhow::{Context as _, Result, bail};
|
||||
use fabro_config::merge::combine_files;
|
||||
use fabro_types::settings::cli::CliLayer;
|
||||
use fabro_types::settings::{CliSettings, SettingsLayer};
|
||||
use fabro_util::printer::Printer;
|
||||
use tokio::sync::OnceCell;
|
||||
|
|
@ -34,34 +36,66 @@ pub(crate) struct CommandContext {
|
|||
}
|
||||
|
||||
impl CommandContext {
|
||||
pub(crate) fn base(printer: Printer) -> Result<Self> {
|
||||
Self::new(printer, ServerMode::None)
|
||||
pub(crate) fn base(
|
||||
printer: Printer,
|
||||
cli_settings: CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
) -> Result<Self> {
|
||||
Self::new(printer, ServerMode::None, cli_settings, cli_layer)
|
||||
}
|
||||
|
||||
pub(crate) fn for_target(args: &ServerTargetArgs, printer: Printer) -> Result<Self> {
|
||||
Self::new(printer, ServerMode::ByTarget {
|
||||
target_override: args.server.clone(),
|
||||
})
|
||||
pub(crate) fn for_target(
|
||||
args: &ServerTargetArgs,
|
||||
printer: Printer,
|
||||
cli_settings: CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
) -> Result<Self> {
|
||||
Self::new(
|
||||
printer,
|
||||
ServerMode::ByTarget {
|
||||
target_override: args.server.clone(),
|
||||
},
|
||||
cli_settings,
|
||||
cli_layer,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn for_connection(args: &ServerConnectionArgs, printer: Printer) -> Result<Self> {
|
||||
Self::new(printer, ServerMode::ByStorageDir {
|
||||
target_override: args.target.server.clone(),
|
||||
storage_dir_override: args.storage_dir.clone_path(),
|
||||
})
|
||||
pub(crate) fn for_connection(
|
||||
args: &ServerConnectionArgs,
|
||||
printer: Printer,
|
||||
cli_settings: CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
) -> Result<Self> {
|
||||
Self::new(
|
||||
printer,
|
||||
ServerMode::ByStorageDir {
|
||||
target_override: args.target.server.clone(),
|
||||
storage_dir_override: args.storage_dir.clone_path(),
|
||||
},
|
||||
cli_settings,
|
||||
cli_layer,
|
||||
)
|
||||
}
|
||||
|
||||
fn new(printer: Printer, server_mode: ServerMode) -> Result<Self> {
|
||||
fn new(
|
||||
printer: Printer,
|
||||
server_mode: ServerMode,
|
||||
cli_settings: CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
) -> Result<Self> {
|
||||
let cwd = std::env::current_dir().context("Failed to get current directory")?;
|
||||
let base_config_path = user_config::active_settings_path(None);
|
||||
let machine_settings = match &server_mode {
|
||||
let disk_settings = match &server_mode {
|
||||
ServerMode::None | ServerMode::ByTarget { .. } => user_config::load_settings()?,
|
||||
ServerMode::ByStorageDir {
|
||||
storage_dir_override,
|
||||
..
|
||||
} => user_config::load_settings_with_storage_dir(storage_dir_override.as_deref())?,
|
||||
};
|
||||
let cli_settings = user_config::resolve_cli_settings(&machine_settings)?;
|
||||
let machine_settings = combine_files(disk_settings, SettingsLayer {
|
||||
cli: Some(cli_layer.clone()),
|
||||
..SettingsLayer::default()
|
||||
});
|
||||
|
||||
Ok(Self {
|
||||
printer,
|
||||
|
|
|
|||
|
|
@ -1,15 +1,18 @@
|
|||
use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::{Context, Result, bail};
|
||||
use fabro_types::settings::CliSettings;
|
||||
use fabro_types::settings::cli::{CliLayer, OutputFormat};
|
||||
use fabro_util::printer::Printer;
|
||||
|
||||
use crate::args::{ArtifactCpArgs, GlobalArgs};
|
||||
use crate::args::ArtifactCpArgs;
|
||||
use crate::server_client::ServerStoreClient;
|
||||
use crate::shared::{print_json_pretty, split_run_path};
|
||||
|
||||
pub(super) async fn cp_command(
|
||||
args: &ArtifactCpArgs,
|
||||
globals: &GlobalArgs,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
let (run_id_selector, asset_path) = parse_source(&args.source);
|
||||
|
|
@ -18,6 +21,8 @@ pub(super) async fn cp_command(
|
|||
run_id_selector,
|
||||
args.node.as_deref(),
|
||||
args.retry,
|
||||
cli,
|
||||
cli_layer,
|
||||
printer,
|
||||
)
|
||||
.await?;
|
||||
|
|
@ -55,7 +60,7 @@ pub(super) async fn cp_command(
|
|||
.unwrap_or_else(|| std::ffi::OsStr::new(&entry.relative_path)),
|
||||
);
|
||||
write_artifact_file(&client, &run_id, entry, &dest_file).await?;
|
||||
if globals.json {
|
||||
if cli.output.format == OutputFormat::Json {
|
||||
print_json_pretty(&serde_json::json!({
|
||||
"copied": [{
|
||||
"relative_path": entry.relative_path,
|
||||
|
|
@ -116,7 +121,7 @@ pub(super) async fn cp_command(
|
|||
}
|
||||
}
|
||||
|
||||
if globals.json {
|
||||
if cli.output.format == OutputFormat::Json {
|
||||
print_json_pretty(&serde_json::json!({ "copied": copied }))?;
|
||||
} else {
|
||||
fabro_util::printout!(
|
||||
|
|
|
|||
|
|
@ -1,14 +1,17 @@
|
|||
use anyhow::Result;
|
||||
use cli_table::format::{Border, Justify, Separator};
|
||||
use cli_table::{Cell, CellStruct, Style, Table};
|
||||
use fabro_types::settings::CliSettings;
|
||||
use fabro_types::settings::cli::{CliLayer, OutputFormat};
|
||||
use fabro_util::printer::Printer;
|
||||
use fabro_util::terminal::Styles;
|
||||
|
||||
use crate::args::{ArtifactListArgs, GlobalArgs};
|
||||
use crate::args::ArtifactListArgs;
|
||||
|
||||
pub(super) async fn list_command(
|
||||
args: &ArtifactListArgs,
|
||||
globals: &GlobalArgs,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
let (_run_id, _client, entries) = super::resolve_artifacts(
|
||||
|
|
@ -16,11 +19,13 @@ pub(super) async fn list_command(
|
|||
&args.run_id,
|
||||
args.node.as_deref(),
|
||||
args.retry,
|
||||
cli,
|
||||
cli_layer,
|
||||
printer,
|
||||
)
|
||||
.await?;
|
||||
|
||||
if globals.json {
|
||||
if cli.output.format == OutputFormat::Json {
|
||||
fabro_util::printout!(printer, "{}", serde_json::to_string_pretty(&entries)?);
|
||||
return Ok(());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,10 +2,12 @@ mod cp;
|
|||
mod list;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use fabro_types::settings::CliSettings;
|
||||
use fabro_types::settings::cli::CliLayer;
|
||||
use fabro_types::{RunId, StageId};
|
||||
use fabro_util::printer::Printer;
|
||||
|
||||
use crate::args::{ArtifactCommand, ArtifactNamespace, GlobalArgs, ServerTargetArgs};
|
||||
use crate::args::{ArtifactCommand, ArtifactNamespace, ServerTargetArgs};
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::server_client::ServerStoreClient;
|
||||
use crate::server_runs::ServerSummaryLookup;
|
||||
|
|
@ -25,9 +27,11 @@ pub(super) async fn resolve_artifacts(
|
|||
run_selector: &str,
|
||||
node: Option<&str>,
|
||||
retry: Option<u32>,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
printer: Printer,
|
||||
) -> Result<(RunId, ServerStoreClient, Vec<ArtifactEntry>)> {
|
||||
let ctx = CommandContext::for_target(server, printer)?;
|
||||
let ctx = CommandContext::for_target(server, printer, cli.clone(), cli_layer)?;
|
||||
let lookup = ServerSummaryLookup::from_client(ctx.server().await?).await?;
|
||||
let run = lookup.resolve(run_selector)?;
|
||||
let run_id = run.run_id();
|
||||
|
|
@ -64,11 +68,12 @@ pub(super) async fn resolve_artifacts(
|
|||
|
||||
pub(crate) async fn dispatch(
|
||||
ns: ArtifactNamespace,
|
||||
globals: &GlobalArgs,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
match ns.command {
|
||||
ArtifactCommand::List(args) => list::list_command(&args, globals, printer).await,
|
||||
ArtifactCommand::Cp(args) => cp::cp_command(&args, globals, printer).await,
|
||||
ArtifactCommand::List(args) => list::list_command(&args, cli, cli_layer, printer).await,
|
||||
ArtifactCommand::Cp(args) => cp::cp_command(&args, cli, cli_layer, printer).await,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,10 +3,11 @@ use std::path::Path;
|
|||
|
||||
use fabro_config::effective_settings::{EffectiveSettingsLayers, EffectiveSettingsMode};
|
||||
use fabro_config::{load_and_resolve, load_settings_project, project};
|
||||
use fabro_types::settings::SettingsLayer;
|
||||
use fabro_types::settings::cli::{CliLayer, OutputFormat};
|
||||
use fabro_types::settings::{CliSettings, SettingsLayer};
|
||||
use fabro_util::printer::Printer;
|
||||
|
||||
use crate::args::{GlobalArgs, SettingsArgs};
|
||||
use crate::args::SettingsArgs;
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::shared::print_json_pretty;
|
||||
use crate::user_config;
|
||||
|
|
@ -76,9 +77,11 @@ fn strip_nulls(value: &mut serde_json::Value) {
|
|||
|
||||
fn local_settings_value(
|
||||
args: &SettingsArgs,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
printer: Printer,
|
||||
) -> anyhow::Result<serde_json::Value> {
|
||||
let base_ctx = CommandContext::base(printer)?;
|
||||
let base_ctx = CommandContext::base(printer, cli.clone(), cli_layer)?;
|
||||
let layers = config_layers(&base_ctx, args.workflow.as_deref())?;
|
||||
let mut value = serde_json::to_value(load_and_resolve(
|
||||
layers,
|
||||
|
|
@ -91,15 +94,17 @@ fn local_settings_value(
|
|||
|
||||
async fn rendered_config(
|
||||
args: &SettingsArgs,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
printer: Printer,
|
||||
) -> anyhow::Result<serde_json::Value> {
|
||||
if args.local {
|
||||
return local_settings_value(args, printer);
|
||||
return local_settings_value(args, cli, cli_layer, printer);
|
||||
}
|
||||
if args.workflow.is_some() {
|
||||
anyhow::bail!("WORKFLOW requires --local; use `fabro settings --local WORKFLOW`");
|
||||
}
|
||||
let ctx = CommandContext::for_target(&args.target, printer)?;
|
||||
let ctx = CommandContext::for_target(&args.target, printer, cli.clone(), cli_layer)?;
|
||||
ctx.server()
|
||||
.await?
|
||||
.retrieve_resolved_server_settings()
|
||||
|
|
@ -108,11 +113,12 @@ async fn rendered_config(
|
|||
|
||||
pub(crate) async fn execute(
|
||||
args: &SettingsArgs,
|
||||
globals: &GlobalArgs,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
printer: Printer,
|
||||
) -> anyhow::Result<()> {
|
||||
let config = Box::pin(rendered_config(args, printer)).await?;
|
||||
if globals.json {
|
||||
let config = Box::pin(rendered_config(args, cli, cli_layer, printer)).await?;
|
||||
if cli.output.format == OutputFormat::Json {
|
||||
print_json_pretty(&config)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ use fabro_config::user::{
|
|||
active_settings_path, legacy_old_user_config_path, legacy_server_config_path,
|
||||
legacy_user_config_path,
|
||||
};
|
||||
use fabro_types::settings::CliSettings;
|
||||
use fabro_types::settings::cli::{CliLayer, OutputFormat};
|
||||
pub(crate) use fabro_util::check_report::{
|
||||
CheckDetail, CheckReport, CheckResult, CheckSection, CheckStatus,
|
||||
};
|
||||
|
|
@ -14,7 +16,7 @@ use fabro_util::printer::Printer;
|
|||
use fabro_util::terminal::Styles;
|
||||
use fabro_util::version::FABRO_VERSION;
|
||||
|
||||
use crate::args::{DoctorArgs, GlobalArgs};
|
||||
use crate::args::DoctorArgs;
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::shared::print_json_pretty;
|
||||
use crate::user_config;
|
||||
|
|
@ -231,11 +233,13 @@ fn render_report(report: &CheckReport, styles: &Styles, verbose: bool, printer:
|
|||
pub(crate) async fn run_doctor(
|
||||
args: &DoctorArgs,
|
||||
verbose: bool,
|
||||
globals: &GlobalArgs,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
printer: Printer,
|
||||
) -> Result<i32, anyhow::Error> {
|
||||
let styles = Styles::detect_stdout();
|
||||
let spinner = if globals.json {
|
||||
let json = cli.output.format == OutputFormat::Json;
|
||||
let spinner = if json {
|
||||
None
|
||||
} else {
|
||||
let spinner = indicatif::ProgressBar::new_spinner();
|
||||
|
|
@ -290,7 +294,7 @@ pub(crate) async fn run_doctor(
|
|||
}],
|
||||
};
|
||||
|
||||
let ctx = match CommandContext::for_target(&args.target, printer) {
|
||||
let ctx = match CommandContext::for_target(&args.target, printer, cli.clone(), cli_layer) {
|
||||
Ok(ctx) => ctx,
|
||||
Err(err) => {
|
||||
report.sections.push(CheckSection {
|
||||
|
|
@ -311,7 +315,7 @@ pub(crate) async fn run_doctor(
|
|||
spinner.finish_and_clear();
|
||||
}
|
||||
|
||||
if globals.json {
|
||||
if json {
|
||||
print_json_pretty(&report)?;
|
||||
} else {
|
||||
render_report(&report, &styles, verbose, printer);
|
||||
|
|
@ -341,7 +345,7 @@ pub(crate) async fn run_doctor(
|
|||
spinner.finish_and_clear();
|
||||
}
|
||||
|
||||
if globals.json {
|
||||
if json {
|
||||
print_json_pretty(&report)?;
|
||||
} else {
|
||||
render_report(&report, &styles, verbose, printer);
|
||||
|
|
@ -370,7 +374,7 @@ pub(crate) async fn run_doctor(
|
|||
spinner.finish_and_clear();
|
||||
}
|
||||
|
||||
if globals.json {
|
||||
if json {
|
||||
print_json_pretty(&report)?;
|
||||
} else {
|
||||
render_report(&report, &styles, verbose, printer);
|
||||
|
|
@ -411,7 +415,7 @@ pub(crate) async fn run_doctor(
|
|||
spinner.finish_and_clear();
|
||||
}
|
||||
|
||||
if globals.json {
|
||||
if json {
|
||||
print_json_pretty(&report)?;
|
||||
} else {
|
||||
render_report(&report, &styles, verbose, printer);
|
||||
|
|
|
|||
|
|
@ -6,12 +6,12 @@ use fabro_agent::cli::{OutputFormat, run_with_args, run_with_args_and_client};
|
|||
use fabro_llm::client::Client;
|
||||
use fabro_llm::providers::FabroServerAdapter;
|
||||
use fabro_mcp::config::{McpServerSettings, McpTransport};
|
||||
use fabro_types::settings::InterpString;
|
||||
use fabro_types::settings::cli::OutputFormat as SettingsOutputFormat;
|
||||
use fabro_types::settings::run::McpEntryLayer;
|
||||
use fabro_types::settings::{CliSettings, InterpString};
|
||||
use fabro_util::printer::Printer;
|
||||
|
||||
use crate::args::{ExecArgs, GlobalArgs};
|
||||
use crate::args::ExecArgs;
|
||||
use crate::user_config;
|
||||
|
||||
fn runtime_mcp_server(name: &str, entry: &McpEntryLayer) -> McpServerSettings {
|
||||
|
|
@ -100,34 +100,28 @@ fn runtime_mcp_server(name: &str, entry: &McpEntryLayer) -> McpServerSettings {
|
|||
|
||||
pub(crate) async fn execute(
|
||||
mut args: ExecArgs,
|
||||
globals: &GlobalArgs,
|
||||
cli: &CliSettings,
|
||||
_printer: Printer,
|
||||
) -> Result<()> {
|
||||
use fabro_agent::cli::PermissionLevel as AgentPermissionLevel;
|
||||
use fabro_types::settings::run::AgentPermissions;
|
||||
|
||||
let cli_settings = user_config::load_settings()?;
|
||||
let resolved_cli = user_config::resolve_cli_settings(&cli_settings)?;
|
||||
let raw_settings = user_config::load_settings()?;
|
||||
#[cfg(feature = "sleep_inhibitor")]
|
||||
let _sleep_guard = crate::sleep_inhibitor::guard(resolved_cli.exec.prevent_idle_sleep);
|
||||
let provider_str = resolved_cli
|
||||
let _sleep_guard = crate::sleep_inhibitor::guard(cli.exec.prevent_idle_sleep);
|
||||
let provider_str = cli
|
||||
.exec
|
||||
.model
|
||||
.provider
|
||||
.as_ref()
|
||||
.map(InterpString::as_source);
|
||||
let model_str = resolved_cli
|
||||
.exec
|
||||
.model
|
||||
.name
|
||||
.as_ref()
|
||||
.map(InterpString::as_source);
|
||||
let permissions = resolved_cli.exec.agent.permissions.map(|p| match p {
|
||||
let model_str = cli.exec.model.name.as_ref().map(InterpString::as_source);
|
||||
let permissions = cli.exec.agent.permissions.map(|p| match p {
|
||||
AgentPermissions::ReadOnly => AgentPermissionLevel::ReadOnly,
|
||||
AgentPermissions::ReadWrite => AgentPermissionLevel::ReadWrite,
|
||||
AgentPermissions::Full => AgentPermissionLevel::Full,
|
||||
});
|
||||
let output_format = Some(match resolved_cli.output.format {
|
||||
let output_format = Some(match cli.output.format {
|
||||
SettingsOutputFormat::Text => OutputFormat::Text,
|
||||
SettingsOutputFormat::Json => OutputFormat::Json,
|
||||
});
|
||||
|
|
@ -137,16 +131,12 @@ pub(crate) async fn execute(
|
|||
permissions,
|
||||
output_format,
|
||||
);
|
||||
if globals.json {
|
||||
args.agent.output_format = Some(OutputFormat::Json);
|
||||
}
|
||||
let server_target = user_config::exec_server_target(&args.server, &cli_settings)?;
|
||||
let server_target = user_config::exec_server_target(&args.server, &raw_settings)?;
|
||||
// v2 MCPs live under `cli.exec.agent.mcps` (owner-specific) or
|
||||
// `run.agent.mcps`. For `fabro exec` we use the cli.exec path, falling
|
||||
// back to run.agent.mcps if unset.
|
||||
let mcp_servers: Vec<McpServerSettings> = if !resolved_cli.exec.agent.mcps.is_empty() {
|
||||
resolved_cli
|
||||
.exec
|
||||
let mcp_servers: Vec<McpServerSettings> = if !cli.exec.agent.mcps.is_empty() {
|
||||
cli.exec
|
||||
.agent
|
||||
.mcps
|
||||
.values()
|
||||
|
|
@ -157,7 +147,7 @@ pub(crate) async fn execute(
|
|||
tool_timeout_secs: server.tool_timeout_secs,
|
||||
})
|
||||
.collect()
|
||||
} else if let Some(mcps) = cli_settings
|
||||
} else if let Some(mcps) = raw_settings
|
||||
.cli
|
||||
.as_ref()
|
||||
.and_then(|cli| cli.exec.as_ref())
|
||||
|
|
@ -169,7 +159,7 @@ pub(crate) async fn execute(
|
|||
.map(|(name, entry)| runtime_mcp_server(name, entry))
|
||||
.collect()
|
||||
} else {
|
||||
fabro_config::resolve_run_from_file(&cli_settings)
|
||||
fabro_config::resolve_run_from_file(&raw_settings)
|
||||
.map(|settings| {
|
||||
settings
|
||||
.agent
|
||||
|
|
|
|||
|
|
@ -4,12 +4,13 @@ use anyhow::bail;
|
|||
use fabro_api::types;
|
||||
use fabro_config::load::load_settings_user;
|
||||
use fabro_config::user::active_settings_path;
|
||||
use fabro_types::settings::SettingsLayer;
|
||||
use fabro_types::settings::cli::{CliLayer, OutputFormat};
|
||||
use fabro_types::settings::{CliSettings, SettingsLayer};
|
||||
use fabro_util::printer::Printer;
|
||||
use fabro_util::terminal::Styles;
|
||||
use tracing::debug;
|
||||
|
||||
use crate::args::{GlobalArgs, GraphArgs, GraphDirection};
|
||||
use crate::args::{GraphArgs, GraphDirection, require_no_json_override};
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::commands::run::output::api_diagnostics_to_local;
|
||||
use crate::manifest_builder::{ManifestBuildInput, build_run_manifest};
|
||||
|
|
@ -18,14 +19,16 @@ use crate::shared::{absolute_or_current, print_diagnostics, print_json_pretty, r
|
|||
pub(crate) async fn run(
|
||||
args: &GraphArgs,
|
||||
styles: &Styles,
|
||||
globals: &GlobalArgs,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
process_local_json: bool,
|
||||
printer: Printer,
|
||||
) -> anyhow::Result<()> {
|
||||
if globals.json && args.output.is_none() {
|
||||
globals.require_no_json()?;
|
||||
if process_local_json && args.output.is_none() {
|
||||
require_no_json_override(process_local_json)?;
|
||||
}
|
||||
|
||||
let ctx = CommandContext::for_target(&args.target, printer)?;
|
||||
let ctx = CommandContext::for_target(&args.target, printer, cli.clone(), cli_layer)?;
|
||||
let built = build_run_manifest(ManifestBuildInput {
|
||||
workflow: args.workflow.clone(),
|
||||
cwd: ctx.cwd().to_path_buf(),
|
||||
|
|
@ -60,7 +63,7 @@ pub(crate) async fn run(
|
|||
|
||||
if let Some(ref output_path) = args.output {
|
||||
std::fs::write(output_path, &rendered)?;
|
||||
if globals.json {
|
||||
if cli.output.format == OutputFormat::Json {
|
||||
print_json_pretty(&serde_json::json!({
|
||||
"path": absolute_or_current(output_path),
|
||||
"format": args.format.to_string(),
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ use fabro_auth::{AuthCredential, AuthMethod, codex_oauth_config, credential_id_f
|
|||
use fabro_config::user::SETTINGS_CONFIG_FILENAME;
|
||||
use fabro_config::{Storage, envfile, legacy_env};
|
||||
use fabro_model::Provider;
|
||||
use fabro_types::settings::CliSettings;
|
||||
use fabro_types::settings::cli::{CliLayer, OutputFormat};
|
||||
use fabro_util::printer::Printer;
|
||||
use fabro_util::terminal::Styles;
|
||||
use fabro_util::{dev_token, session_secret};
|
||||
|
|
@ -32,8 +34,7 @@ use tokio::task::spawn_blocking;
|
|||
|
||||
use super::doctor;
|
||||
use crate::args::{
|
||||
DoctorArgs, GlobalArgs, InstallArgs, InstallGitHubStrategyArg, InstallNonInteractiveArgs,
|
||||
ServerTargetArgs,
|
||||
DoctorArgs, InstallArgs, InstallGitHubStrategyArg, InstallNonInteractiveArgs, ServerTargetArgs,
|
||||
};
|
||||
use crate::commands::server::{record, stop};
|
||||
use crate::gh::GhCli;
|
||||
|
|
@ -1229,15 +1230,18 @@ async fn persist_install_outputs_with_settings(
|
|||
|
||||
pub(crate) async fn run_install(
|
||||
args: &InstallArgs,
|
||||
globals: &GlobalArgs,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
process_local_json: bool,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
if globals.json && !args.non_interactive {
|
||||
let json = cli.output.format == OutputFormat::Json;
|
||||
if process_local_json && !args.non_interactive {
|
||||
bail!("--json is only supported for install with --non-interactive");
|
||||
}
|
||||
|
||||
let result = Box::pin(run_install_inner(args, globals, printer)).await;
|
||||
if globals.json {
|
||||
let result = Box::pin(run_install_inner(args, cli, cli_layer, printer)).await;
|
||||
if json {
|
||||
let emit_result = match &result {
|
||||
Ok(()) => emit_install_json_event(&install_complete_event()),
|
||||
Err(err) => emit_install_json_event(&install_error_event(&err.to_string())),
|
||||
|
|
@ -1252,9 +1256,11 @@ pub(crate) async fn run_install(
|
|||
|
||||
async fn run_install_inner(
|
||||
args: &InstallArgs,
|
||||
globals: &GlobalArgs,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
let json = cli.output.format == OutputFormat::Json;
|
||||
let web_url = &args.web_url;
|
||||
let s = Styles::detect_stderr();
|
||||
let emoji = console::Emoji("⚒️ ", "");
|
||||
|
|
@ -1360,7 +1366,7 @@ async fn run_install_inner(
|
|||
} else {
|
||||
GitHubAppHandoffMode::Interactive
|
||||
},
|
||||
globals.json,
|
||||
json,
|
||||
printer,
|
||||
)
|
||||
.await?;
|
||||
|
|
@ -1539,7 +1545,7 @@ async fn run_install_inner(
|
|||
target: ServerTargetArgs::default(),
|
||||
verbose: true,
|
||||
};
|
||||
let _ = doctor::run_doctor(&doctor_args, true, globals, printer).await?;
|
||||
let _ = doctor::run_doctor(&doctor_args, true, cli, cli_layer, printer).await?;
|
||||
}
|
||||
|
||||
fabro_util::printerr!(printer, "");
|
||||
|
|
|
|||
|
|
@ -3,12 +3,14 @@ use cli_table::format::{Border, Justify, Separator};
|
|||
use cli_table::{Cell, CellStruct, Color, Style, Table};
|
||||
use fabro_api::{self, types as api_types};
|
||||
use fabro_model::{Catalog, Model, Provider};
|
||||
use fabro_types::settings::CliSettings;
|
||||
use fabro_types::settings::cli::{CliLayer, OutputFormat};
|
||||
use fabro_util::printer::Printer;
|
||||
use fabro_util::terminal::Styles;
|
||||
use serde::Serialize;
|
||||
use serde::de::DeserializeOwned;
|
||||
|
||||
use crate::args::{GlobalArgs, ModelListArgs, ModelTestArgs, ModelsCommand};
|
||||
use crate::args::{ModelListArgs, ModelTestArgs, ModelsCommand};
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::server_client;
|
||||
|
||||
|
|
@ -41,7 +43,8 @@ struct ModelTestOutput {
|
|||
|
||||
pub(crate) async fn execute(
|
||||
command: Option<ModelsCommand>,
|
||||
globals: &GlobalArgs,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
let command = command.unwrap_or_default();
|
||||
|
|
@ -49,10 +52,15 @@ pub(crate) async fn execute(
|
|||
ModelsCommand::List(args) => &args.target,
|
||||
ModelsCommand::Test(args) => &args.target,
|
||||
};
|
||||
let ctx = CommandContext::for_target(target_args, printer)?;
|
||||
let ctx = CommandContext::for_target(target_args, printer, cli.clone(), cli_layer)?;
|
||||
let server = ctx.server().await?;
|
||||
|
||||
run_models(command, server.api(), globals.json).await
|
||||
run_models(
|
||||
command,
|
||||
server.api(),
|
||||
cli.output.format == OutputFormat::Json,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
fn format_context_window(tokens: i64) -> String {
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@ use std::io::Write;
|
|||
|
||||
use fabro_config::project::resolve_workflow;
|
||||
use fabro_graphviz::parser::parse_ast;
|
||||
use fabro_types::settings::CliSettings;
|
||||
use fabro_util::printer::Printer;
|
||||
|
||||
use crate::args::{GlobalArgs, ParseArgs};
|
||||
use crate::args::ParseArgs;
|
||||
use crate::shared::read_workflow_file;
|
||||
|
||||
pub(crate) fn run(args: &ParseArgs, globals: &GlobalArgs, _printer: Printer) -> anyhow::Result<()> {
|
||||
let _ = globals;
|
||||
pub(crate) fn run(args: &ParseArgs, _cli: &CliSettings, _printer: Printer) -> anyhow::Result<()> {
|
||||
let stdout = std::io::stdout();
|
||||
run_to(args, stdout.lock())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,22 @@
|
|||
use anyhow::Result;
|
||||
use fabro_types::settings::CliSettings;
|
||||
use fabro_types::settings::cli::{CliLayer, OutputFormat};
|
||||
use fabro_util::printer::Printer;
|
||||
use tracing::info;
|
||||
|
||||
use crate::args::{GlobalArgs, PrCloseArgs};
|
||||
use crate::args::PrCloseArgs;
|
||||
use crate::shared::print_json_pretty;
|
||||
|
||||
pub(super) async fn close_command(
|
||||
args: PrCloseArgs,
|
||||
globals: &GlobalArgs,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
let (record, _run_id) = super::load_pr_record(&args.server, &args.run_id, printer).await?;
|
||||
let (record, _run_id) =
|
||||
super::load_pr_record(&args.server, &args.run_id, cli, cli_layer, printer).await?;
|
||||
|
||||
let creds = super::load_github_credentials_required(printer)?;
|
||||
let creds = super::load_github_credentials_required(cli, cli_layer, printer)?;
|
||||
|
||||
fabro_github::close_pull_request(
|
||||
&creds,
|
||||
|
|
@ -25,7 +29,7 @@ pub(super) async fn close_command(
|
|||
.map_err(|err| anyhow::anyhow!("{err}"))?;
|
||||
|
||||
info!(number = record.number, owner = %record.owner, repo = %record.repo, "Closed pull request");
|
||||
if globals.json {
|
||||
if cli.output.format == OutputFormat::Json {
|
||||
print_json_pretty(&serde_json::json!({
|
||||
"number": record.number,
|
||||
"html_url": record.html_url,
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
use anyhow::{Context, Result, bail};
|
||||
use fabro_model::Catalog;
|
||||
use fabro_sandbox::daytona::detect_repo_info;
|
||||
use fabro_types::settings::CliSettings;
|
||||
use fabro_types::settings::cli::{CliLayer, OutputFormat};
|
||||
use fabro_util::printer::Printer;
|
||||
use fabro_workflow::outcome::StageStatus;
|
||||
use fabro_workflow::pull_request::maybe_open_pull_request;
|
||||
use tracing::info;
|
||||
|
||||
use crate::args::{GlobalArgs, PrCreateArgs};
|
||||
use crate::args::PrCreateArgs;
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::commands::store::rebuild::rebuild_run_store;
|
||||
use crate::server_runs::ServerSummaryLookup;
|
||||
|
|
@ -15,10 +17,11 @@ use crate::shared::repo::ensure_matching_repo_origin;
|
|||
|
||||
pub(super) async fn create_command(
|
||||
args: PrCreateArgs,
|
||||
globals: &GlobalArgs,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
let ctx = CommandContext::for_target(&args.server, printer)?;
|
||||
let ctx = CommandContext::for_target(&args.server, printer, cli.clone(), cli_layer)?;
|
||||
let lookup = ServerSummaryLookup::from_client(ctx.server().await?).await?;
|
||||
let run = lookup.resolve(&args.run_id)?;
|
||||
let run_id = run.run_id();
|
||||
|
|
@ -74,7 +77,7 @@ pub(super) async fn create_command(
|
|||
let (owner, repo) = fabro_github::parse_github_owner_repo(&https_url)
|
||||
.map_err(|err| anyhow::anyhow!("{err}"))?;
|
||||
|
||||
let creds = super::load_github_credentials_required(printer)?;
|
||||
let creds = super::load_github_credentials_required(cli, cli_layer, printer)?;
|
||||
|
||||
let branch_found = fabro_github::branch_exists(
|
||||
&creds,
|
||||
|
|
@ -116,14 +119,14 @@ pub(super) async fn create_command(
|
|||
match record {
|
||||
Some(record) => {
|
||||
info!(pr_url = %record.html_url, "Pull request created");
|
||||
if globals.json {
|
||||
if cli.output.format == OutputFormat::Json {
|
||||
print_json_pretty(&record)?;
|
||||
} else {
|
||||
fabro_util::printout!(printer, "{}", record.html_url);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
if globals.json {
|
||||
if cli.output.format == OutputFormat::Json {
|
||||
print_json_pretty(&serde_json::Value::Null)?;
|
||||
} else {
|
||||
fabro_util::printout!(printer, "No pull request created (empty diff).");
|
||||
|
|
|
|||
|
|
@ -1,13 +1,15 @@
|
|||
use anyhow::Result;
|
||||
use cli_table::format::{Border, Separator};
|
||||
use cli_table::{Cell, CellStruct, Color, Style, Table};
|
||||
use fabro_types::settings::CliSettings;
|
||||
use fabro_types::settings::cli::{CliLayer, OutputFormat};
|
||||
use fabro_util::printer::Printer;
|
||||
use fabro_util::terminal::Styles;
|
||||
use futures::future::join_all;
|
||||
use serde::Serialize;
|
||||
use tracing::info;
|
||||
|
||||
use crate::args::{GlobalArgs, PrListArgs};
|
||||
use crate::args::PrListArgs;
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::server_runs::ServerSummaryLookup;
|
||||
use crate::shared::{color_if, print_json_pretty};
|
||||
|
|
@ -23,10 +25,11 @@ struct PrRow {
|
|||
|
||||
pub(super) async fn list_command(
|
||||
args: PrListArgs,
|
||||
globals: &GlobalArgs,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
let ctx = CommandContext::for_target(&args.server, printer)?;
|
||||
let ctx = CommandContext::for_target(&args.server, printer, cli.clone(), cli_layer)?;
|
||||
let lookup = ServerSummaryLookup::from_client(ctx.server().await?).await?;
|
||||
|
||||
let mut entries = Vec::new();
|
||||
|
|
@ -39,7 +42,7 @@ pub(super) async fn list_command(
|
|||
}
|
||||
|
||||
if entries.is_empty() {
|
||||
if globals.json {
|
||||
if cli.output.format == OutputFormat::Json {
|
||||
print_json_pretty(&Vec::<PrRow>::new())?;
|
||||
return Ok(());
|
||||
}
|
||||
|
|
@ -47,7 +50,7 @@ pub(super) async fn list_command(
|
|||
return Ok(());
|
||||
}
|
||||
|
||||
let creds = super::load_github_credentials_required(printer)?;
|
||||
let creds = super::load_github_credentials_required(cli, cli_layer, printer)?;
|
||||
|
||||
let futures: Vec<_> = entries
|
||||
.iter()
|
||||
|
|
@ -101,7 +104,7 @@ pub(super) async fn list_command(
|
|||
.collect()
|
||||
};
|
||||
|
||||
if globals.json {
|
||||
if cli.output.format == OutputFormat::Json {
|
||||
print_json_pretty(&rows)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,22 @@
|
|||
use anyhow::Result;
|
||||
use fabro_types::settings::CliSettings;
|
||||
use fabro_types::settings::cli::{CliLayer, OutputFormat};
|
||||
use fabro_util::printer::Printer;
|
||||
use tracing::info;
|
||||
|
||||
use crate::args::{GlobalArgs, PrMergeArgs};
|
||||
use crate::args::PrMergeArgs;
|
||||
use crate::shared::print_json_pretty;
|
||||
|
||||
pub(super) async fn merge_command(
|
||||
args: PrMergeArgs,
|
||||
globals: &GlobalArgs,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
let (record, _run_id) = super::load_pr_record(&args.server, &args.run_id, printer).await?;
|
||||
let (record, _run_id) =
|
||||
super::load_pr_record(&args.server, &args.run_id, cli, cli_layer, printer).await?;
|
||||
|
||||
let creds = super::load_github_credentials_required(printer)?;
|
||||
let creds = super::load_github_credentials_required(cli, cli_layer, printer)?;
|
||||
|
||||
fabro_github::merge_pull_request(
|
||||
&creds,
|
||||
|
|
@ -26,7 +30,7 @@ pub(super) async fn merge_command(
|
|||
.map_err(|err| anyhow::anyhow!("{err}"))?;
|
||||
|
||||
info!(number = record.number, owner = %record.owner, repo = %record.repo, method = %args.method, "Merged pull request");
|
||||
if globals.json {
|
||||
if cli.output.format == OutputFormat::Json {
|
||||
print_json_pretty(&serde_json::json!({
|
||||
"number": record.number,
|
||||
"html_url": record.html_url,
|
||||
|
|
|
|||
|
|
@ -8,10 +8,11 @@ use anyhow::{Context, Result, anyhow};
|
|||
use fabro_config::Storage;
|
||||
use fabro_github::GitHubCredentials;
|
||||
use fabro_types::PullRequestRecord;
|
||||
use fabro_types::settings::InterpString;
|
||||
use fabro_types::settings::cli::CliLayer;
|
||||
use fabro_types::settings::{CliSettings, InterpString};
|
||||
use fabro_util::printer::Printer;
|
||||
|
||||
use crate::args::{GlobalArgs, PrCommand, PrNamespace, ServerTargetArgs};
|
||||
use crate::args::{PrCommand, PrNamespace, ServerTargetArgs};
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::server_runs::ServerSummaryLookup;
|
||||
use crate::shared::github::build_github_credentials;
|
||||
|
|
@ -22,20 +23,27 @@ const GITHUB_CREDENTIALS_REQUIRED: &str =
|
|||
|
||||
pub(crate) async fn dispatch(
|
||||
ns: PrNamespace,
|
||||
globals: &GlobalArgs,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
match ns.command {
|
||||
PrCommand::Create(args) => Box::pin(create::create_command(args, globals, printer)).await,
|
||||
PrCommand::List(args) => list::list_command(args, globals, printer).await,
|
||||
PrCommand::View(args) => view::view_command(args, globals, printer).await,
|
||||
PrCommand::Merge(args) => merge::merge_command(args, globals, printer).await,
|
||||
PrCommand::Close(args) => close::close_command(args, globals, printer).await,
|
||||
PrCommand::Create(args) => {
|
||||
Box::pin(create::create_command(args, cli, cli_layer, printer)).await
|
||||
}
|
||||
PrCommand::List(args) => list::list_command(args, cli, cli_layer, printer).await,
|
||||
PrCommand::View(args) => view::view_command(args, cli, cli_layer, printer).await,
|
||||
PrCommand::Merge(args) => merge::merge_command(args, cli, cli_layer, printer).await,
|
||||
PrCommand::Close(args) => close::close_command(args, cli, cli_layer, printer).await,
|
||||
}
|
||||
}
|
||||
|
||||
fn load_github_credentials_required(printer: Printer) -> Result<GitHubCredentials> {
|
||||
let ctx = CommandContext::base(printer)?;
|
||||
fn load_github_credentials_required(
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
printer: Printer,
|
||||
) -> Result<GitHubCredentials> {
|
||||
let ctx = CommandContext::base(printer, cli.clone(), cli_layer)?;
|
||||
let server_settings =
|
||||
fabro_config::resolve_server_from_file(ctx.machine_settings()).map_err(|errors| {
|
||||
anyhow!(
|
||||
|
|
@ -68,9 +76,11 @@ fn load_github_credentials_required(printer: Printer) -> Result<GitHubCredential
|
|||
pub(crate) async fn load_pr_record(
|
||||
server: &ServerTargetArgs,
|
||||
run_id: &str,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
printer: Printer,
|
||||
) -> Result<(PullRequestRecord, fabro_types::RunId)> {
|
||||
let ctx = CommandContext::for_target(server, printer)?;
|
||||
let ctx = CommandContext::for_target(server, printer, cli.clone(), cli_layer)?;
|
||||
let lookup = ServerSummaryLookup::from_client(ctx.server().await?).await?;
|
||||
let run = lookup.resolve(run_id)?;
|
||||
let run_id = run.run_id();
|
||||
|
|
|
|||
|
|
@ -1,18 +1,22 @@
|
|||
use anyhow::Result;
|
||||
use fabro_types::settings::CliSettings;
|
||||
use fabro_types::settings::cli::{CliLayer, OutputFormat};
|
||||
use fabro_util::printer::Printer;
|
||||
use tracing::info;
|
||||
|
||||
use crate::args::{GlobalArgs, PrViewArgs};
|
||||
use crate::args::PrViewArgs;
|
||||
use crate::shared::print_json_pretty;
|
||||
|
||||
pub(super) async fn view_command(
|
||||
args: PrViewArgs,
|
||||
globals: &GlobalArgs,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
let (record, _run_id) = super::load_pr_record(&args.server, &args.run_id, printer).await?;
|
||||
let (record, _run_id) =
|
||||
super::load_pr_record(&args.server, &args.run_id, cli, cli_layer, printer).await?;
|
||||
|
||||
let creds = super::load_github_credentials_required(printer)?;
|
||||
let creds = super::load_github_credentials_required(cli, cli_layer, printer)?;
|
||||
|
||||
let detail = fabro_github::get_pull_request(
|
||||
&creds,
|
||||
|
|
@ -26,7 +30,7 @@ pub(super) async fn view_command(
|
|||
|
||||
info!(number = detail.number, owner = %record.owner, repo = %record.repo, "Viewing pull request");
|
||||
|
||||
if globals.json {
|
||||
if cli.output.format == OutputFormat::Json {
|
||||
print_json_pretty(&detail)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
use anyhow::bail;
|
||||
use fabro_config::load::load_settings_user;
|
||||
use fabro_config::user::active_settings_path;
|
||||
use fabro_types::settings::cli::OutputVerbosity;
|
||||
use fabro_types::settings::CliSettings;
|
||||
use fabro_types::settings::cli::{CliLayer, OutputFormat, OutputVerbosity};
|
||||
use fabro_util::printer::Printer;
|
||||
use fabro_util::terminal::Styles;
|
||||
|
||||
use crate::args::{GlobalArgs, PreflightArgs};
|
||||
use crate::args::PreflightArgs;
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::commands::run::output::{
|
||||
api_check_report_to_local, api_diagnostics_to_local, print_preflight_workflow_summary,
|
||||
|
|
@ -16,12 +17,13 @@ use crate::shared::print_json_pretty;
|
|||
|
||||
pub(crate) async fn execute(
|
||||
mut args: PreflightArgs,
|
||||
globals: &GlobalArgs,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
printer: Printer,
|
||||
) -> anyhow::Result<()> {
|
||||
let styles: &'static Styles = Box::leak(Box::new(Styles::detect_stderr()));
|
||||
let ctx = CommandContext::for_target(&args.target, printer)?;
|
||||
args.verbose = args.verbose || ctx.cli_settings().output.verbosity == OutputVerbosity::Verbose;
|
||||
let ctx = CommandContext::for_target(&args.target, printer, cli.clone(), cli_layer)?;
|
||||
args.verbose = args.verbose || cli.output.verbosity == OutputVerbosity::Verbose;
|
||||
|
||||
let manifest = build_run_manifest(ManifestBuildInput {
|
||||
workflow: args.workflow.clone(),
|
||||
|
|
@ -36,7 +38,7 @@ pub(crate) async fn execute(
|
|||
let response = client.run_preflight(manifest.manifest).await?;
|
||||
let diagnostics = api_diagnostics_to_local(&response.workflow.diagnostics);
|
||||
|
||||
if globals.json {
|
||||
if cli.output.format == OutputFormat::Json {
|
||||
print_json_pretty(&response)?;
|
||||
} else {
|
||||
print_preflight_workflow_summary(
|
||||
|
|
|
|||
|
|
@ -2,21 +2,25 @@ use anyhow::Result;
|
|||
use fabro_api::types;
|
||||
use fabro_auth::credential_id_for;
|
||||
use fabro_config::legacy_env;
|
||||
use fabro_types::settings::CliSettings;
|
||||
use fabro_types::settings::cli::CliLayer;
|
||||
use fabro_util::printer::Printer;
|
||||
use fabro_util::terminal::Styles;
|
||||
|
||||
use crate::args::{GlobalArgs, ProviderLoginArgs};
|
||||
use crate::args::{ProviderLoginArgs, require_no_json_override};
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::shared::provider_auth;
|
||||
|
||||
pub(super) async fn login_command(
|
||||
args: ProviderLoginArgs,
|
||||
globals: &GlobalArgs,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
process_local_json: bool,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
globals.require_no_json()?;
|
||||
require_no_json_override(process_local_json)?;
|
||||
let s = Styles::detect_stderr();
|
||||
let ctx = CommandContext::for_target(&args.target, printer)?;
|
||||
let ctx = CommandContext::for_target(&args.target, printer, cli.clone(), cli_layer)?;
|
||||
let server = ctx.server().await?;
|
||||
let credential = if args.api_key_stdin {
|
||||
provider_auth::authenticate_provider_with_api_key_source(
|
||||
|
|
|
|||
|
|
@ -1,16 +1,22 @@
|
|||
mod login;
|
||||
|
||||
use anyhow::Result;
|
||||
use fabro_types::settings::CliSettings;
|
||||
use fabro_types::settings::cli::CliLayer;
|
||||
use fabro_util::printer::Printer;
|
||||
|
||||
use crate::args::{GlobalArgs, ProviderCommand, ProviderNamespace};
|
||||
use crate::args::{ProviderCommand, ProviderNamespace};
|
||||
|
||||
pub(crate) async fn dispatch(
|
||||
ns: ProviderNamespace,
|
||||
globals: &GlobalArgs,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
process_local_json: bool,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
match ns.command {
|
||||
ProviderCommand::Login(args) => login::login_command(args, globals, printer).await,
|
||||
ProviderCommand::Login(args) => {
|
||||
login::login_command(args, cli, cli_layer, process_local_json, printer).await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
use anyhow::{Context, Result, bail};
|
||||
use fabro_types::settings::CliSettings;
|
||||
use fabro_types::settings::cli::OutputFormat;
|
||||
use fabro_util::printer::Printer;
|
||||
|
||||
use crate::args::GlobalArgs;
|
||||
|
||||
pub(crate) fn run_deinit(globals: &GlobalArgs, printer: Printer) -> Result<Vec<String>> {
|
||||
pub(crate) fn run_deinit(cli: &CliSettings, printer: Printer) -> Result<Vec<String>> {
|
||||
let repo_root = super::init::git_repo_root()?;
|
||||
let mut removed = Vec::new();
|
||||
|
||||
|
|
@ -20,7 +20,7 @@ pub(crate) fn run_deinit(globals: &GlobalArgs, printer: Printer) -> Result<Vec<S
|
|||
std::fs::remove_dir_all(&fabro_dir)
|
||||
.with_context(|| format!("failed to remove {}", fabro_dir.display()))?;
|
||||
removed.push(".fabro/".to_string());
|
||||
if !globals.json {
|
||||
if cli.output.format != OutputFormat::Json {
|
||||
fabro_util::printerr!(
|
||||
printer,
|
||||
" {} {}",
|
||||
|
|
@ -29,7 +29,7 @@ pub(crate) fn run_deinit(globals: &GlobalArgs, printer: Printer) -> Result<Vec<S
|
|||
);
|
||||
}
|
||||
|
||||
if !globals.json {
|
||||
if cli.output.format != OutputFormat::Json {
|
||||
fabro_util::printerr!(
|
||||
printer,
|
||||
"\n{}",
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::{Context, Result, bail};
|
||||
use fabro_types::settings::CliSettings;
|
||||
use fabro_types::settings::cli::{CliLayer, OutputFormat};
|
||||
use fabro_util::printer::Printer;
|
||||
use tokio::process::Command as TokioCommand;
|
||||
use tokio::task::spawn_blocking;
|
||||
|
||||
use crate::args::{GlobalArgs, RepoInitArgs, ServerTargetArgs};
|
||||
use crate::args::{RepoInitArgs, ServerTargetArgs};
|
||||
use crate::command_context::CommandContext;
|
||||
|
||||
#[expect(
|
||||
|
|
@ -29,7 +31,8 @@ pub(super) fn git_repo_root() -> Result<PathBuf> {
|
|||
|
||||
pub(crate) async fn run_init(
|
||||
args: &RepoInitArgs,
|
||||
globals: &GlobalArgs,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
printer: Printer,
|
||||
) -> Result<Vec<String>> {
|
||||
let repo_root = spawn_blocking(git_repo_root)
|
||||
|
|
@ -71,7 +74,7 @@ draft = true
|
|||
let green = console::Style::new().green();
|
||||
let bold = console::Style::new().bold();
|
||||
let dim = console::Style::new().dim();
|
||||
if !globals.json {
|
||||
if cli.output.format != OutputFormat::Json {
|
||||
fabro_util::printerr!(
|
||||
printer,
|
||||
" {} {}",
|
||||
|
|
@ -104,7 +107,7 @@ draft = true
|
|||
)
|
||||
.with_context(|| format!("failed to write {}", dot_path.display()))?;
|
||||
created.push(".fabro/workflows/hello/workflow.fabro".to_string());
|
||||
if !globals.json {
|
||||
if cli.output.format != OutputFormat::Json {
|
||||
fabro_util::printerr!(
|
||||
printer,
|
||||
" {} {}",
|
||||
|
|
@ -121,7 +124,7 @@ draft = true
|
|||
)
|
||||
.with_context(|| format!("failed to write {}", toml_path.display()))?;
|
||||
created.push(".fabro/workflows/hello/workflow.toml".to_string());
|
||||
if !globals.json {
|
||||
if cli.output.format != OutputFormat::Json {
|
||||
fabro_util::printerr!(
|
||||
printer,
|
||||
" {} {}",
|
||||
|
|
@ -130,7 +133,7 @@ draft = true
|
|||
);
|
||||
}
|
||||
|
||||
if !globals.json {
|
||||
if cli.output.format != OutputFormat::Json {
|
||||
fabro_util::printerr!(
|
||||
printer,
|
||||
"\n{} Run a workflow with:\n\n {}",
|
||||
|
|
@ -142,14 +145,19 @@ draft = true
|
|||
);
|
||||
}
|
||||
|
||||
if !globals.json {
|
||||
check_github_app_installation(&args.target, printer).await;
|
||||
if cli.output.format != OutputFormat::Json {
|
||||
check_github_app_installation(&args.target, cli, cli_layer, printer).await;
|
||||
}
|
||||
|
||||
Ok(created)
|
||||
}
|
||||
|
||||
async fn check_github_app_installation(target: &ServerTargetArgs, printer: Printer) {
|
||||
async fn check_github_app_installation(
|
||||
target: &ServerTargetArgs,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
printer: Printer,
|
||||
) {
|
||||
// Get the git remote origin URL
|
||||
let output = match TokioCommand::new("git")
|
||||
.args(["remote", "get-url", "origin"])
|
||||
|
|
@ -187,7 +195,7 @@ async fn check_github_app_installation(target: &ServerTargetArgs, printer: Print
|
|||
return; // Not a GitHub repo — skip silently
|
||||
};
|
||||
|
||||
let ctx = match CommandContext::for_target(target, printer) {
|
||||
let ctx = match CommandContext::for_target(target, printer, cli.clone(), cli_layer) {
|
||||
Ok(ctx) => ctx,
|
||||
Err(err) => {
|
||||
fabro_util::printerr!(
|
||||
|
|
|
|||
|
|
@ -2,27 +2,30 @@ pub(crate) mod deinit;
|
|||
pub(crate) mod init;
|
||||
|
||||
use anyhow::Result;
|
||||
use fabro_types::settings::CliSettings;
|
||||
use fabro_types::settings::cli::{CliLayer, OutputFormat};
|
||||
use fabro_util::printer::Printer;
|
||||
|
||||
use crate::args::{GlobalArgs, RepoCommand, RepoNamespace};
|
||||
use crate::args::{RepoCommand, RepoNamespace};
|
||||
use crate::shared::print_json_pretty;
|
||||
|
||||
pub(crate) async fn dispatch(
|
||||
ns: RepoNamespace,
|
||||
globals: &GlobalArgs,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
match ns.command {
|
||||
RepoCommand::Init(args) => {
|
||||
let created = init::run_init(&args, globals, printer).await?;
|
||||
if globals.json {
|
||||
let created = init::run_init(&args, cli, cli_layer, printer).await?;
|
||||
if cli.output.format == OutputFormat::Json {
|
||||
print_json_pretty(&serde_json::json!({ "created": created }))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
RepoCommand::Deinit => {
|
||||
let removed = deinit::run_deinit(globals, printer)?;
|
||||
if globals.json {
|
||||
let removed = deinit::run_deinit(cli, printer)?;
|
||||
if cli.output.format == OutputFormat::Json {
|
||||
print_json_pretty(&serde_json::json!({ "removed": removed }))?;
|
||||
}
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -1,27 +1,34 @@
|
|||
use anyhow::Result;
|
||||
use fabro_types::settings::cli::OutputVerbosity;
|
||||
use fabro_types::settings::CliSettings;
|
||||
use fabro_types::settings::cli::{CliLayer, OutputFormat, OutputVerbosity};
|
||||
use fabro_util::printer::Printer;
|
||||
use fabro_util::terminal::Styles;
|
||||
|
||||
use crate::args::{GlobalArgs, RunArgs};
|
||||
use crate::args::RunArgs;
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::shared::print_json_pretty;
|
||||
use crate::user_config::settings_layer_with_storage_dir;
|
||||
|
||||
pub(crate) async fn execute(
|
||||
mut args: RunArgs,
|
||||
globals: &GlobalArgs,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
let styles: &'static Styles = Box::leak(Box::new(Styles::detect_stderr()));
|
||||
let ctx = CommandContext::for_target(&args.target, printer)?;
|
||||
let cli = settings_layer_with_storage_dir(None)?;
|
||||
args.verbose = args.verbose || ctx.cli_settings().output.verbosity == OutputVerbosity::Verbose;
|
||||
let ctx = CommandContext::for_target(&args.target, printer, cli.clone(), cli_layer)?;
|
||||
let cli_defaults = settings_layer_with_storage_dir(None)?;
|
||||
args.verbose = args.verbose || cli.output.verbosity == OutputVerbosity::Verbose;
|
||||
|
||||
let quiet = args.detach;
|
||||
let prevent_idle_sleep = ctx.cli_settings().exec.prevent_idle_sleep;
|
||||
let created_run = Box::pin(super::create::create_run(
|
||||
&ctx, &args, cli, styles, quiet, printer,
|
||||
&ctx,
|
||||
&args,
|
||||
cli_defaults,
|
||||
styles,
|
||||
quiet,
|
||||
printer,
|
||||
))
|
||||
.await?;
|
||||
|
||||
|
|
@ -34,8 +41,9 @@ pub(crate) async fn execute(
|
|||
let client = ctx.server().await?;
|
||||
super::start::start_run_with_client(&client, &created_run.run_id, false).await?;
|
||||
|
||||
let json = cli.output.format == OutputFormat::Json;
|
||||
if args.detach {
|
||||
if globals.json {
|
||||
if json {
|
||||
print_json_pretty(&serde_json::json!({ "run_id": created_run.run_id }))?;
|
||||
} else {
|
||||
fabro_util::printout!(printer, "{}", created_run.run_id);
|
||||
|
|
@ -46,11 +54,11 @@ pub(crate) async fn execute(
|
|||
&created_run.run_id,
|
||||
true,
|
||||
styles,
|
||||
globals.json,
|
||||
json,
|
||||
printer,
|
||||
)
|
||||
.await?;
|
||||
if !globals.json {
|
||||
if !json {
|
||||
super::output::print_run_summary_with_client(
|
||||
&client,
|
||||
&created_run.run_id,
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::{Context, Result, bail};
|
||||
use fabro_types::settings::CliSettings;
|
||||
use fabro_types::settings::cli::{CliLayer, OutputFormat};
|
||||
use fabro_util::printer::Printer;
|
||||
use tokio::fs;
|
||||
use tracing::{debug, info};
|
||||
|
||||
use crate::args::{CpArgs, GlobalArgs, ServerTargetArgs};
|
||||
use crate::args::{CpArgs, ServerTargetArgs};
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::server_client::ServerStoreClient;
|
||||
use crate::server_runs::ServerSummaryLookup;
|
||||
|
|
@ -25,7 +27,12 @@ enum CopyDirection {
|
|||
},
|
||||
}
|
||||
|
||||
pub(crate) async fn cp_command(args: CpArgs, globals: &GlobalArgs, printer: Printer) -> Result<()> {
|
||||
pub(crate) async fn cp_command(
|
||||
args: CpArgs,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
let direction = parse_direction(&args.src, &args.dst)?;
|
||||
|
||||
match direction {
|
||||
|
|
@ -35,7 +42,8 @@ pub(crate) async fn cp_command(args: CpArgs, globals: &GlobalArgs, printer: Prin
|
|||
local_path,
|
||||
} => {
|
||||
let (client, run_id) =
|
||||
resolve_client_and_run_id(&args.server, &run_prefix, printer).await?;
|
||||
resolve_client_and_run_id(&args.server, &run_prefix, cli, cli_layer, printer)
|
||||
.await?;
|
||||
|
||||
let file_count = if args.recursive {
|
||||
Some(download_recursive(&client, &run_id, &remote_path, &local_path).await?)
|
||||
|
|
@ -45,7 +53,7 @@ pub(crate) async fn cp_command(args: CpArgs, globals: &GlobalArgs, printer: Prin
|
|||
None
|
||||
};
|
||||
|
||||
if globals.json {
|
||||
if cli.output.format == OutputFormat::Json {
|
||||
let mut value = serde_json::json!({
|
||||
"direction": "download",
|
||||
"recursive": args.recursive,
|
||||
|
|
@ -66,7 +74,8 @@ pub(crate) async fn cp_command(args: CpArgs, globals: &GlobalArgs, printer: Prin
|
|||
remote_path,
|
||||
} => {
|
||||
let (client, run_id) =
|
||||
resolve_client_and_run_id(&args.server, &run_prefix, printer).await?;
|
||||
resolve_client_and_run_id(&args.server, &run_prefix, cli, cli_layer, printer)
|
||||
.await?;
|
||||
|
||||
let file_count = if args.recursive {
|
||||
Some(upload_recursive(&client, &run_id, &local_path, &remote_path).await?)
|
||||
|
|
@ -76,7 +85,7 @@ pub(crate) async fn cp_command(args: CpArgs, globals: &GlobalArgs, printer: Prin
|
|||
None
|
||||
};
|
||||
|
||||
if globals.json {
|
||||
if cli.output.format == OutputFormat::Json {
|
||||
let mut value = serde_json::json!({
|
||||
"direction": "upload",
|
||||
"recursive": args.recursive,
|
||||
|
|
@ -120,9 +129,11 @@ fn parse_direction(src: &str, dst: &str) -> Result<CopyDirection> {
|
|||
async fn resolve_client_and_run_id(
|
||||
server: &ServerTargetArgs,
|
||||
run_prefix: &str,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
printer: Printer,
|
||||
) -> Result<(ServerStoreClient, fabro_types::RunId)> {
|
||||
let ctx = CommandContext::for_target(server, printer)?;
|
||||
let ctx = CommandContext::for_target(server, printer, cli.clone(), cli_layer)?;
|
||||
let lookup = ServerSummaryLookup::from_client(ctx.server().await?).await?;
|
||||
let run = lookup.resolve(run_prefix)?;
|
||||
Ok((lookup.client().clone_for_reuse(), run.run_id()))
|
||||
|
|
|
|||
|
|
@ -1,18 +1,25 @@
|
|||
use std::io::{self, IsTerminal, Write};
|
||||
|
||||
use anyhow::{Context, Result, bail};
|
||||
use fabro_types::settings::CliSettings;
|
||||
use fabro_types::settings::cli::{CliLayer, OutputFormat};
|
||||
use fabro_util::printer::Printer;
|
||||
use tracing::{debug, info};
|
||||
|
||||
use crate::args::{DiffArgs, GlobalArgs};
|
||||
use crate::args::DiffArgs;
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::server_client::RunProjection;
|
||||
use crate::server_runs::ServerSummaryLookup;
|
||||
use crate::shared::print_json_pretty;
|
||||
|
||||
pub(crate) async fn run(args: DiffArgs, globals: &GlobalArgs, printer: Printer) -> Result<()> {
|
||||
pub(crate) async fn run(
|
||||
args: DiffArgs,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
info!(run_id = %args.run, "Showing diff");
|
||||
let ctx = CommandContext::for_target(&args.server, printer)?;
|
||||
let ctx = CommandContext::for_target(&args.server, printer, cli.clone(), cli_layer)?;
|
||||
let lookup = ServerSummaryLookup::from_client(ctx.server().await?).await?;
|
||||
let run = lookup.resolve(&args.run)?;
|
||||
let run_id = run.run_id();
|
||||
|
|
@ -20,7 +27,7 @@ pub(crate) async fn run(args: DiffArgs, globals: &GlobalArgs, printer: Printer)
|
|||
|
||||
let patch = resolve_diff(&state, &args)?;
|
||||
|
||||
if globals.json {
|
||||
if cli.output.format == OutputFormat::Json {
|
||||
let value = serde_json::json!({
|
||||
"run_id": run_id,
|
||||
"node": args.node,
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
use anyhow::{Context, Result};
|
||||
use fabro_checkpoint::git::Store;
|
||||
use fabro_types::settings::CliSettings;
|
||||
use fabro_types::settings::cli::{CliLayer, OutputFormat};
|
||||
use fabro_util::printer::Printer;
|
||||
use fabro_util::terminal::Styles;
|
||||
use fabro_workflow::operations::{ForkRunInput, RewindTarget, build_timeline_or_rebuild, fork};
|
||||
use git2::Repository;
|
||||
|
||||
use crate::args::{ForkArgs, GlobalArgs};
|
||||
use crate::args::ForkArgs;
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::commands::store::rebuild::rebuild_run_store;
|
||||
use crate::server_runs::ServerSummaryLookup;
|
||||
|
|
@ -15,11 +17,12 @@ use crate::shared::repo::ensure_matching_repo_origin;
|
|||
pub(crate) async fn run(
|
||||
args: &ForkArgs,
|
||||
styles: &Styles,
|
||||
globals: &GlobalArgs,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
let repo = Repository::discover(".").context("not in a git repository")?;
|
||||
let ctx = CommandContext::for_target(&args.server, printer)?;
|
||||
let ctx = CommandContext::for_target(&args.server, printer, cli.clone(), cli_layer)?;
|
||||
let lookup = ServerSummaryLookup::from_client(ctx.server().await?).await?;
|
||||
let run = lookup.resolve(&args.run_id)?;
|
||||
let run_id = run.run_id();
|
||||
|
|
@ -33,7 +36,7 @@ pub(crate) async fn run(
|
|||
let timeline = build_timeline_or_rebuild(&store, Some(&run_store), &run_id).await?;
|
||||
|
||||
if args.list {
|
||||
if globals.json {
|
||||
if cli.output.format == OutputFormat::Json {
|
||||
print_json_pretty(&super::rewind::timeline_entries_json(&timeline))?;
|
||||
return Ok(());
|
||||
}
|
||||
|
|
@ -55,7 +58,7 @@ pub(crate) async fn run(
|
|||
let run_id_string = run_id.to_string();
|
||||
let new_run_id_string = new_run_id.to_string();
|
||||
|
||||
if globals.json {
|
||||
if cli.output.format == OutputFormat::Json {
|
||||
let target = args.target.clone().unwrap_or_else(|| "latest".to_string());
|
||||
print_json_pretty(&serde_json::json!({
|
||||
"source_run_id": run_id_string,
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ use std::time::Duration;
|
|||
|
||||
use anyhow::{Context, Result, bail};
|
||||
use chrono::{DateTime, Utc};
|
||||
use fabro_types::settings::CliSettings;
|
||||
use fabro_types::settings::cli::{CliLayer, OutputFormat};
|
||||
use fabro_util::json::normalize_json_value;
|
||||
use fabro_util::printer::Printer;
|
||||
use fabro_util::redact::redact_jsonl_line;
|
||||
|
|
@ -11,7 +13,7 @@ use fabro_util::terminal::Styles;
|
|||
use tokio::time;
|
||||
use tracing::{debug, info};
|
||||
|
||||
use crate::args::{GlobalArgs, LogsArgs};
|
||||
use crate::args::LogsArgs;
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::server_client;
|
||||
use crate::server_runs::ServerSummaryLookup;
|
||||
|
|
@ -22,10 +24,11 @@ const FOLLOW_TERMINAL_GRACE: Duration = Duration::from_millis(500);
|
|||
pub(crate) async fn run(
|
||||
args: &LogsArgs,
|
||||
styles: &Styles,
|
||||
globals: &GlobalArgs,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
let ctx = CommandContext::for_target(&args.server, printer)?;
|
||||
let ctx = CommandContext::for_target(&args.server, printer, cli.clone(), cli_layer)?;
|
||||
let lookup = ServerSummaryLookup::from_client(ctx.server().await?).await?;
|
||||
let run = lookup.resolve(&args.run)?;
|
||||
let client = lookup.client();
|
||||
|
|
@ -52,7 +55,7 @@ pub(crate) async fn run(
|
|||
let stdout = io::stdout();
|
||||
let is_tty = stdout.is_terminal();
|
||||
let mut out = stdout.lock();
|
||||
let pretty = args.pretty && !globals.json;
|
||||
let pretty = args.pretty && cli.output.format != OutputFormat::Json;
|
||||
|
||||
for line in &filtered {
|
||||
if pretty {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
use anyhow::Result;
|
||||
use fabro_types::settings::CliSettings;
|
||||
use fabro_types::settings::cli::{CliLayer, OutputFormat};
|
||||
use fabro_util::printer::Printer;
|
||||
use fabro_util::terminal::Styles;
|
||||
|
||||
use crate::args::{AttachArgs, GlobalArgs, RunArgs, RunCommands, RunWorkerArgs, StartArgs};
|
||||
use crate::args::{AttachArgs, RunCommands, RunWorkerArgs, StartArgs};
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::server_runs::ServerSummaryLookup;
|
||||
use crate::shared::print_json_pretty;
|
||||
|
|
@ -26,30 +28,29 @@ pub(crate) mod ssh;
|
|||
pub(crate) mod start;
|
||||
pub(crate) mod wait;
|
||||
|
||||
fn apply_json_defaults(args: &mut RunArgs, globals: &GlobalArgs) {
|
||||
if globals.json {
|
||||
args.auto_approve = true;
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn dispatch(
|
||||
cmd: RunCommands,
|
||||
globals: &GlobalArgs,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
_process_local_json: bool,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
match cmd {
|
||||
RunCommands::Run(mut args) => {
|
||||
apply_json_defaults(&mut args, globals);
|
||||
Box::pin(command::execute(args, globals, printer)).await
|
||||
}
|
||||
RunCommands::Create(mut args) => {
|
||||
apply_json_defaults(&mut args, globals);
|
||||
RunCommands::Run(args) => Box::pin(command::execute(args, cli, cli_layer, printer)).await,
|
||||
RunCommands::Create(args) => {
|
||||
let styles: &'static Styles = Box::leak(Box::new(Styles::detect_stderr()));
|
||||
let cli = settings_layer_with_storage_dir(None)?;
|
||||
let ctx = CommandContext::for_target(&args.target, printer)?;
|
||||
let created_run =
|
||||
Box::pin(create::create_run(&ctx, &args, cli, styles, true, printer)).await?;
|
||||
if globals.json {
|
||||
let cli_defaults = settings_layer_with_storage_dir(None)?;
|
||||
let ctx = CommandContext::for_target(&args.target, printer, cli.clone(), cli_layer)?;
|
||||
let created_run = Box::pin(create::create_run(
|
||||
&ctx,
|
||||
&args,
|
||||
cli_defaults,
|
||||
styles,
|
||||
true,
|
||||
printer,
|
||||
))
|
||||
.await?;
|
||||
if cli.output.format == OutputFormat::Json {
|
||||
print_json_pretty(&serde_json::json!({ "run_id": created_run.run_id }))?;
|
||||
} else {
|
||||
fabro_util::printout!(printer, "{}", created_run.run_id);
|
||||
|
|
@ -57,19 +58,19 @@ pub(crate) async fn dispatch(
|
|||
Ok(())
|
||||
}
|
||||
RunCommands::Start(StartArgs { server, run }) => {
|
||||
let ctx = CommandContext::for_target(&server, printer)?;
|
||||
let ctx = CommandContext::for_target(&server, printer, cli.clone(), cli_layer)?;
|
||||
let lookup = ServerSummaryLookup::from_client(ctx.server().await?).await?;
|
||||
let run_info = lookup.resolve(&run)?;
|
||||
let run_id = run_info.run_id();
|
||||
start::start_run_with_client(lookup.client(), &run_id, false).await?;
|
||||
if globals.json {
|
||||
if cli.output.format == OutputFormat::Json {
|
||||
print_json_pretty(&serde_json::json!({ "run_id": run_id }))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
RunCommands::Attach(AttachArgs { server, run }) => {
|
||||
let styles: &'static Styles = Box::leak(Box::new(Styles::detect_stderr()));
|
||||
let ctx = CommandContext::for_target(&server, printer)?;
|
||||
let ctx = CommandContext::for_target(&server, printer, cli.clone(), cli_layer)?;
|
||||
let lookup = ServerSummaryLookup::from_client(ctx.server().await?).await?;
|
||||
let run_info = lookup.resolve(&run)?;
|
||||
let run_id = run_info.run_id();
|
||||
|
|
@ -78,7 +79,7 @@ pub(crate) async fn dispatch(
|
|||
&run_id,
|
||||
false,
|
||||
styles,
|
||||
globals.json,
|
||||
cli.output.format == OutputFormat::Json,
|
||||
printer,
|
||||
)
|
||||
.await?;
|
||||
|
|
@ -105,31 +106,32 @@ pub(crate) async fn dispatch(
|
|||
)
|
||||
.await
|
||||
}
|
||||
RunCommands::Diff(args) => diff::run(args, globals, printer).await,
|
||||
RunCommands::Diff(args) => diff::run(args, cli, cli_layer, printer).await,
|
||||
RunCommands::Logs(args) => {
|
||||
let styles = Styles::detect_stdout();
|
||||
logs::run(&args, &styles, globals, printer).await
|
||||
logs::run(&args, &styles, cli, cli_layer, printer).await
|
||||
}
|
||||
RunCommands::Resume(args) => {
|
||||
let styles: &'static Styles = Box::leak(Box::new(Styles::detect_stderr()));
|
||||
#[cfg(feature = "sleep_inhibitor")]
|
||||
let _sleep_guard = {
|
||||
let ctx = CommandContext::for_target(&args.server, printer)?;
|
||||
let ctx =
|
||||
CommandContext::for_target(&args.server, printer, cli.clone(), cli_layer)?;
|
||||
crate::sleep_inhibitor::guard(ctx.cli_settings().exec.prevent_idle_sleep)
|
||||
};
|
||||
resume::resume_command(args, styles, globals, printer).await
|
||||
resume::resume_command(args, styles, cli, cli_layer, printer).await
|
||||
}
|
||||
RunCommands::Rewind(args) => {
|
||||
let styles = Styles::detect_stderr();
|
||||
Box::pin(rewind::run(&args, &styles, globals, printer)).await
|
||||
Box::pin(rewind::run(&args, &styles, cli, cli_layer, printer)).await
|
||||
}
|
||||
RunCommands::Fork(args) => {
|
||||
let styles = Styles::detect_stderr();
|
||||
Box::pin(fork::run(&args, &styles, globals, printer)).await
|
||||
Box::pin(fork::run(&args, &styles, cli, cli_layer, printer)).await
|
||||
}
|
||||
RunCommands::Wait(args) => {
|
||||
let styles = Styles::detect_stderr();
|
||||
wait::run(&args, &styles, globals, printer).await
|
||||
wait::run(&args, &styles, cli, cli_layer, printer).await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,22 @@
|
|||
use anyhow::{Context, Result};
|
||||
use fabro_types::settings::CliSettings;
|
||||
use fabro_types::settings::cli::{CliLayer, OutputFormat};
|
||||
use fabro_util::printer::Printer;
|
||||
use tracing::info;
|
||||
|
||||
use crate::args::{GlobalArgs, PreviewArgs};
|
||||
use crate::args::PreviewArgs;
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::server_runs::ServerSummaryLookup;
|
||||
use crate::shared::print_json_pretty;
|
||||
|
||||
pub(crate) async fn run(args: PreviewArgs, globals: &GlobalArgs, printer: Printer) -> Result<()> {
|
||||
let ctx = CommandContext::for_target(&args.server, printer)?;
|
||||
pub(crate) async fn run(
|
||||
args: PreviewArgs,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
process_local_json: bool,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
let ctx = CommandContext::for_target(&args.server, printer, cli.clone(), cli_layer)?;
|
||||
let lookup = ServerSummaryLookup::from_client(ctx.server().await?).await?;
|
||||
let run = lookup.resolve(&args.run)?;
|
||||
let run_id = run.run_id();
|
||||
|
|
@ -26,7 +34,7 @@ pub(crate) async fn run(args: PreviewArgs, globals: &GlobalArgs, printer: Printe
|
|||
|
||||
info!(run_id = %args.run, port = args.port, "Generating preview URL");
|
||||
|
||||
if globals.json {
|
||||
if cli.output.format == OutputFormat::Json {
|
||||
match response.token {
|
||||
Some(token) => {
|
||||
print_json_pretty(&serde_json::json!({ "url": response.url, "token": token }))?;
|
||||
|
|
@ -51,7 +59,7 @@ pub(crate) async fn run(args: PreviewArgs, globals: &GlobalArgs, printer: Printe
|
|||
}
|
||||
}
|
||||
|
||||
if args.open && !globals.json {
|
||||
if args.open && !process_local_json {
|
||||
#[expect(
|
||||
clippy::disallowed_methods,
|
||||
reason = "Preview URL opening is a fire-and-forget OS integration, not a Tokio-managed child process."
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
use fabro_types::settings::CliSettings;
|
||||
use fabro_types::settings::cli::{CliLayer, OutputFormat};
|
||||
use fabro_util::printer::Printer;
|
||||
use fabro_util::terminal::Styles;
|
||||
|
||||
use crate::args::{GlobalArgs, ResumeArgs};
|
||||
use crate::args::ResumeArgs;
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::server_runs::ServerSummaryLookup;
|
||||
use crate::shared::print_json_pretty;
|
||||
|
|
@ -14,18 +16,20 @@ use crate::shared::print_json_pretty;
|
|||
pub(crate) async fn resume_command(
|
||||
args: ResumeArgs,
|
||||
styles: &'static Styles,
|
||||
globals: &GlobalArgs,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
printer: Printer,
|
||||
) -> anyhow::Result<()> {
|
||||
let ctx = CommandContext::for_target(&args.server, printer)?;
|
||||
let ctx = CommandContext::for_target(&args.server, printer, cli.clone(), cli_layer)?;
|
||||
let lookup = ServerSummaryLookup::from_client(ctx.server().await?).await?;
|
||||
let run = lookup.resolve(&args.run)?;
|
||||
let run_id = run.run_id();
|
||||
|
||||
super::start::start_run_with_client(lookup.client(), &run_id, true).await?;
|
||||
|
||||
let json = cli.output.format == OutputFormat::Json;
|
||||
if args.detach {
|
||||
if globals.json {
|
||||
if json {
|
||||
print_json_pretty(&serde_json::json!({ "run_id": run_id }))?;
|
||||
} else {
|
||||
fabro_util::printout!(printer, "{run_id}");
|
||||
|
|
@ -36,11 +40,11 @@ pub(crate) async fn resume_command(
|
|||
&run_id,
|
||||
true,
|
||||
styles,
|
||||
globals.json,
|
||||
json,
|
||||
printer,
|
||||
)
|
||||
.await?;
|
||||
if !globals.json {
|
||||
if !json {
|
||||
super::output::print_run_summary_with_client(
|
||||
lookup.client(),
|
||||
&run_id,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ use cli_table::format::{Border, Separator};
|
|||
use cli_table::{Cell, CellStruct, Color, Style, Table};
|
||||
use fabro_checkpoint::git::Store;
|
||||
use fabro_types::run_event::{CheckpointCompletedProps, RunRewoundProps, RunSubmittedProps};
|
||||
use fabro_types::settings::CliSettings;
|
||||
use fabro_types::settings::cli::{CliLayer, OutputFormat};
|
||||
use fabro_types::{EventBody, RunEvent};
|
||||
use fabro_util::printer::Printer;
|
||||
use fabro_util::terminal::Styles;
|
||||
|
|
@ -13,7 +15,7 @@ use fabro_workflow::operations::{
|
|||
use git2::Repository;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::args::{GlobalArgs, RewindArgs};
|
||||
use crate::args::RewindArgs;
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::commands::store::rebuild::rebuild_run_store;
|
||||
use crate::server_client::ServerStoreClient;
|
||||
|
|
@ -32,11 +34,12 @@ pub(crate) struct TimelineEntryJson {
|
|||
pub(crate) async fn run(
|
||||
args: &RewindArgs,
|
||||
styles: &Styles,
|
||||
globals: &GlobalArgs,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
let repo = Repository::discover(".").context("not in a git repository")?;
|
||||
let ctx = CommandContext::for_target(&args.server, printer)?;
|
||||
let ctx = CommandContext::for_target(&args.server, printer, cli.clone(), cli_layer)?;
|
||||
let lookup = ServerSummaryLookup::from_client(ctx.server().await?).await?;
|
||||
let run = lookup.resolve(&args.run_id)?;
|
||||
let run_id = run.run_id();
|
||||
|
|
@ -50,7 +53,7 @@ pub(crate) async fn run(
|
|||
let timeline = build_timeline_or_rebuild(&store, Some(&run_store), &run_id).await?;
|
||||
|
||||
if args.list || args.target.is_none() {
|
||||
if globals.json {
|
||||
if cli.output.format == OutputFormat::Json {
|
||||
print_json_pretty(&timeline_entries_json(&timeline))?;
|
||||
return Ok(());
|
||||
}
|
||||
|
|
@ -70,7 +73,7 @@ pub(crate) async fn run(
|
|||
|
||||
let run_id_string = run_id.to_string();
|
||||
|
||||
if globals.json {
|
||||
if cli.output.format == OutputFormat::Json {
|
||||
print_json_pretty(&serde_json::json!({
|
||||
"run_id": run_id_string,
|
||||
"target": args.target.as_deref().unwrap(),
|
||||
|
|
|
|||
|
|
@ -1,18 +1,26 @@
|
|||
use anyhow::{Result, bail};
|
||||
use fabro_types::settings::CliSettings;
|
||||
use fabro_types::settings::cli::{CliLayer, OutputFormat};
|
||||
use fabro_util::printer::Printer;
|
||||
use tracing::info;
|
||||
|
||||
use crate::args::{GlobalArgs, SshArgs};
|
||||
use crate::args::{SshArgs, require_no_json_override};
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::server_runs::ServerSummaryLookup;
|
||||
use crate::shared::print_json_pretty;
|
||||
|
||||
pub(crate) async fn run(args: SshArgs, globals: &GlobalArgs, printer: Printer) -> Result<()> {
|
||||
if globals.json && !args.print {
|
||||
globals.require_no_json()?;
|
||||
pub(crate) async fn run(
|
||||
args: SshArgs,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
process_local_json: bool,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
if process_local_json && !args.print {
|
||||
require_no_json_override(process_local_json)?;
|
||||
}
|
||||
|
||||
let ctx = CommandContext::for_target(&args.server, printer)?;
|
||||
let ctx = CommandContext::for_target(&args.server, printer, cli.clone(), cli_layer)?;
|
||||
let lookup = ServerSummaryLookup::from_client(ctx.server().await?).await?;
|
||||
let run = lookup.resolve(&args.run)?;
|
||||
let run_id = run.run_id();
|
||||
|
|
@ -24,7 +32,7 @@ pub(crate) async fn run(args: SshArgs, globals: &GlobalArgs, printer: Printer) -
|
|||
info!(run_id = %args.run, ttl_minutes = args.ttl, "Creating SSH access");
|
||||
|
||||
if args.print {
|
||||
if globals.json {
|
||||
if cli.output.format == OutputFormat::Json {
|
||||
print_json_pretty(&serde_json::json!({ "command": ssh.command }))?;
|
||||
} else {
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ use std::io::Write;
|
|||
|
||||
use anyhow::{Result, bail};
|
||||
use fabro_types::RunId;
|
||||
use fabro_types::settings::CliSettings;
|
||||
use fabro_types::settings::cli::{CliLayer, OutputFormat};
|
||||
use fabro_util::printer::Printer;
|
||||
use fabro_util::terminal::Styles;
|
||||
use fabro_workflow::records::Conclusion;
|
||||
|
|
@ -9,7 +11,7 @@ use fabro_workflow::run_status::RunStatus;
|
|||
use tokio::time;
|
||||
use tracing::info;
|
||||
|
||||
use crate::args::{GlobalArgs, WaitArgs};
|
||||
use crate::args::WaitArgs;
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::server_runs::ServerSummaryLookup;
|
||||
use crate::shared::{format_duration_ms, format_usd_micros};
|
||||
|
|
@ -22,10 +24,11 @@ const WAIT_STARTUP_GRACE: std::time::Duration = std::time::Duration::from_secs(3
|
|||
pub(crate) async fn run(
|
||||
args: &WaitArgs,
|
||||
styles: &Styles,
|
||||
globals: &GlobalArgs,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
let ctx = CommandContext::for_target(&args.server, printer)?;
|
||||
let ctx = CommandContext::for_target(&args.server, printer, cli.clone(), cli_layer)?;
|
||||
let lookup = ServerSummaryLookup::from_client(ctx.server().await?).await?;
|
||||
let run_info = lookup.resolve(&args.run)?;
|
||||
let client = lookup.client();
|
||||
|
|
@ -74,7 +77,7 @@ pub(crate) async fn run(
|
|||
|
||||
let conclusion = client.get_run_state(&run_id).await?.conclusion;
|
||||
|
||||
if globals.json {
|
||||
if cli.output.format == OutputFormat::Json {
|
||||
let json_value = build_json_output(final_status, &run_id, conclusion.as_ref());
|
||||
let mut out = std::io::stdout().lock();
|
||||
serde_json::to_writer_pretty(&mut out, &json_value)?;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
use anyhow::Result;
|
||||
use fabro_types::settings::CliSettings;
|
||||
use fabro_types::settings::cli::CliLayer;
|
||||
use fabro_util::printer::Printer;
|
||||
use fabro_workflow::run_status::RunStatus;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::args::{GlobalArgs, InspectArgs};
|
||||
use crate::args::InspectArgs;
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::server_client::RunProjection;
|
||||
use crate::server_runs::{ServerRunSummaryInfo, ServerSummaryLookup};
|
||||
|
|
@ -19,8 +21,13 @@ pub(crate) struct InspectOutput {
|
|||
pub sandbox: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
pub(crate) async fn run(args: &InspectArgs, _globals: &GlobalArgs, printer: Printer) -> Result<()> {
|
||||
let ctx = CommandContext::for_target(&args.server, printer)?;
|
||||
pub(crate) async fn run(
|
||||
args: &InspectArgs,
|
||||
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?;
|
||||
let run = lookup.resolve(&args.run)?;
|
||||
let run_id = run.run_id();
|
||||
|
|
|
|||
|
|
@ -4,13 +4,15 @@ use anyhow::Result;
|
|||
use chrono::Utc;
|
||||
use cli_table::format::{Border, Separator};
|
||||
use cli_table::{Cell, CellStruct, Color, Style, Table};
|
||||
use fabro_types::settings::CliSettings;
|
||||
use fabro_types::settings::cli::{CliLayer, OutputFormat};
|
||||
use fabro_util::printer::Printer;
|
||||
use fabro_util::terminal::Styles;
|
||||
use fabro_util::text::strip_goal_decoration;
|
||||
use fabro_workflow::run_status::RunStatus;
|
||||
|
||||
use super::short_run_id;
|
||||
use crate::args::{GlobalArgs, RunsListArgs};
|
||||
use crate::args::RunsListArgs;
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::server_runs::{ServerSummaryLookup, filter_server_runs};
|
||||
use crate::shared::{color_if, format_duration_ms, tilde_path};
|
||||
|
|
@ -18,10 +20,11 @@ use crate::shared::{color_if, format_duration_ms, tilde_path};
|
|||
pub(crate) async fn list_command(
|
||||
args: &RunsListArgs,
|
||||
styles: &Styles,
|
||||
globals: &GlobalArgs,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
let ctx = CommandContext::for_target(&args.server, printer)?;
|
||||
let ctx = CommandContext::for_target(&args.server, printer, cli.clone(), cli_layer)?;
|
||||
let lookup = ServerSummaryLookup::from_client(ctx.server().await?).await?;
|
||||
let label_filters = parse_label_filters(&args.filter.label);
|
||||
let filtered = filter_server_runs(
|
||||
|
|
@ -32,7 +35,7 @@ pub(crate) async fn list_command(
|
|||
!args.all,
|
||||
);
|
||||
|
||||
if globals.json {
|
||||
if cli.output.format == OutputFormat::Json {
|
||||
let json_rows: Vec<_> = filtered
|
||||
.iter()
|
||||
.map(|run| {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
use anyhow::Result;
|
||||
use fabro_types::settings::CliSettings;
|
||||
use fabro_types::settings::cli::CliLayer;
|
||||
use fabro_util::printer::Printer;
|
||||
use fabro_util::terminal::Styles;
|
||||
|
||||
use crate::args::{GlobalArgs, RunsCommands};
|
||||
use crate::args::RunsCommands;
|
||||
|
||||
pub(crate) mod inspect;
|
||||
pub(crate) mod list;
|
||||
|
|
@ -10,16 +12,17 @@ pub(crate) mod rm;
|
|||
|
||||
pub(crate) async fn dispatch(
|
||||
cmd: RunsCommands,
|
||||
globals: &GlobalArgs,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
match cmd {
|
||||
RunsCommands::Ps(args) => {
|
||||
let styles = Styles::detect_stdout();
|
||||
list::list_command(&args, &styles, globals, printer).await
|
||||
list::list_command(&args, &styles, cli, cli_layer, printer).await
|
||||
}
|
||||
RunsCommands::Rm(args) => rm::remove_command(&args, globals, printer).await,
|
||||
RunsCommands::Inspect(args) => inspect::run(&args, globals, printer).await,
|
||||
RunsCommands::Rm(args) => rm::remove_command(&args, cli, cli_layer, printer).await,
|
||||
RunsCommands::Inspect(args) => inspect::run(&args, cli, cli_layer, printer).await,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
use anyhow::{Context, 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::{GlobalArgs, RunsRemoveArgs};
|
||||
use crate::args::RunsRemoveArgs;
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::server_client;
|
||||
use crate::server_runs::{
|
||||
|
|
@ -12,21 +14,23 @@ use crate::shared::print_json_pretty;
|
|||
|
||||
pub(crate) async fn remove_command(
|
||||
args: &RunsRemoveArgs,
|
||||
globals: &GlobalArgs,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
let ctx = CommandContext::for_target(&args.server, printer)?;
|
||||
let ctx = CommandContext::for_target(&args.server, printer, cli.clone(), cli_layer)?;
|
||||
let lookup = ServerSummaryLookup::from_client(ctx.server().await?).await?;
|
||||
remove_from(args, lookup.client(), lookup.runs(), globals, printer).await
|
||||
remove_from(args, lookup.client(), lookup.runs(), cli, printer).await
|
||||
}
|
||||
|
||||
async fn remove_from(
|
||||
args: &RunsRemoveArgs,
|
||||
client: &server_client::ServerStoreClient,
|
||||
runs: &[ServerRunSummaryInfo],
|
||||
globals: &GlobalArgs,
|
||||
cli: &CliSettings,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
let json = cli.output.format == OutputFormat::Json;
|
||||
let mut had_errors = false;
|
||||
let mut removed = Vec::new();
|
||||
let mut errors = Vec::new();
|
||||
|
|
@ -35,7 +39,7 @@ async fn remove_from(
|
|||
let run = match resolve_server_run_from_summaries(runs, identifier) {
|
||||
Ok(run) => run,
|
||||
Err(err) => {
|
||||
if !globals.json {
|
||||
if !json {
|
||||
fabro_util::printerr!(printer, "error: {identifier}: {err}");
|
||||
}
|
||||
errors.push(serde_json::json!({
|
||||
|
|
@ -54,7 +58,7 @@ async fn remove_from(
|
|||
short_run_id(&run_id),
|
||||
run.status()
|
||||
);
|
||||
if !globals.json {
|
||||
if !json {
|
||||
fabro_util::printerr!(printer, "{error}");
|
||||
}
|
||||
errors.push(serde_json::json!({
|
||||
|
|
@ -67,7 +71,7 @@ async fn remove_from(
|
|||
|
||||
let run_id = run.run_id().to_string();
|
||||
if let Err(err) = delete_server_run(client, &run).await {
|
||||
if !globals.json {
|
||||
if !json {
|
||||
fabro_util::printerr!(printer, "error: {identifier}: {err}");
|
||||
}
|
||||
errors.push(serde_json::json!({
|
||||
|
|
@ -78,12 +82,12 @@ async fn remove_from(
|
|||
continue;
|
||||
}
|
||||
removed.push(run_id.clone());
|
||||
if !globals.json {
|
||||
if !json {
|
||||
fabro_util::printerr!(printer, "{}", short_run_id(&run_id));
|
||||
}
|
||||
}
|
||||
|
||||
if globals.json {
|
||||
if json {
|
||||
print_json_pretty(&serde_json::json!({
|
||||
"removed": removed,
|
||||
"errors": errors,
|
||||
|
|
|
|||
|
|
@ -1,16 +1,24 @@
|
|||
use anyhow::Result;
|
||||
use fabro_types::settings::CliSettings;
|
||||
use fabro_types::settings::cli::CliLayer;
|
||||
use fabro_util::printer::Printer;
|
||||
|
||||
use crate::args::{GlobalArgs, SandboxCommand};
|
||||
use crate::args::SandboxCommand;
|
||||
|
||||
pub(crate) async fn dispatch(
|
||||
command: SandboxCommand,
|
||||
globals: &GlobalArgs,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
process_local_json: bool,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
match command {
|
||||
SandboxCommand::Cp(args) => super::run::cp::cp_command(args, globals, printer).await,
|
||||
SandboxCommand::Preview(args) => super::run::preview::run(args, globals, printer).await,
|
||||
SandboxCommand::Ssh(args) => super::run::ssh::run(args, globals, printer).await,
|
||||
SandboxCommand::Cp(args) => super::run::cp::cp_command(args, cli, cli_layer, printer).await,
|
||||
SandboxCommand::Preview(args) => {
|
||||
super::run::preview::run(args, cli, cli_layer, process_local_json, printer).await
|
||||
}
|
||||
SandboxCommand::Ssh(args) => {
|
||||
super::run::ssh::run(args, cli, cli_layer, process_local_json, printer).await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,10 +3,12 @@ use chrono::{DateTime, Utc};
|
|||
use cli_table::format::{Border, Separator};
|
||||
use cli_table::{Cell, CellStruct, Style, Table};
|
||||
use fabro_api::Client;
|
||||
use fabro_types::settings::CliSettings;
|
||||
use fabro_types::settings::cli::OutputFormat;
|
||||
use fabro_util::printer::Printer;
|
||||
use fabro_util::terminal::Styles;
|
||||
|
||||
use crate::args::{GlobalArgs, SecretListArgs};
|
||||
use crate::args::SecretListArgs;
|
||||
use crate::server_client;
|
||||
use crate::shared::print_json_pretty;
|
||||
|
||||
|
|
@ -24,7 +26,7 @@ fn format_age(dt: DateTime<Utc>, now: DateTime<Utc>) -> String {
|
|||
pub(super) async fn list_command(
|
||||
client: &Client,
|
||||
_args: &SecretListArgs,
|
||||
globals: &GlobalArgs,
|
||||
cli: &CliSettings,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
let response = client
|
||||
|
|
@ -33,7 +35,7 @@ pub(super) async fn list_command(
|
|||
.await
|
||||
.map_err(server_client::map_api_error)?;
|
||||
let secrets = response.into_inner().data;
|
||||
if globals.json {
|
||||
if cli.output.format == OutputFormat::Json {
|
||||
print_json_pretty(&secrets)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,23 +3,24 @@ mod rm;
|
|||
mod set;
|
||||
|
||||
use anyhow::Result;
|
||||
use fabro_types::settings::CliSettings;
|
||||
use fabro_types::settings::cli::CliLayer;
|
||||
use fabro_util::printer::Printer;
|
||||
|
||||
use crate::args::{GlobalArgs, SecretCommand, SecretNamespace};
|
||||
use crate::args::{SecretCommand, SecretNamespace};
|
||||
use crate::command_context::CommandContext;
|
||||
|
||||
pub(crate) async fn dispatch(
|
||||
ns: SecretNamespace,
|
||||
globals: &GlobalArgs,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
let ctx = CommandContext::for_target(&ns.target, printer)?;
|
||||
let ctx = CommandContext::for_target(&ns.target, printer, cli.clone(), cli_layer)?;
|
||||
let server = ctx.server().await?;
|
||||
match ns.command {
|
||||
SecretCommand::List(args) => {
|
||||
list::list_command(server.api(), &args, globals, printer).await
|
||||
}
|
||||
SecretCommand::Rm(args) => rm::rm_command(server.api(), &args, globals, printer).await,
|
||||
SecretCommand::Set(args) => set::set_command(server.api(), &args, globals, printer).await,
|
||||
SecretCommand::List(args) => list::list_command(server.api(), &args, cli, printer).await,
|
||||
SecretCommand::Rm(args) => rm::rm_command(server.api(), &args, cli, printer).await,
|
||||
SecretCommand::Set(args) => set::set_command(server.api(), &args, cli, printer).await,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,17 @@
|
|||
use anyhow::Result;
|
||||
use fabro_api::{Client, types};
|
||||
use fabro_types::settings::CliSettings;
|
||||
use fabro_types::settings::cli::OutputFormat;
|
||||
use fabro_util::printer::Printer;
|
||||
|
||||
use crate::args::{GlobalArgs, SecretRmArgs};
|
||||
use crate::args::SecretRmArgs;
|
||||
use crate::server_client;
|
||||
use crate::shared::print_json_pretty;
|
||||
|
||||
pub(super) async fn rm_command(
|
||||
client: &Client,
|
||||
args: &SecretRmArgs,
|
||||
globals: &GlobalArgs,
|
||||
cli: &CliSettings,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
client
|
||||
|
|
@ -20,7 +22,7 @@ pub(super) async fn rm_command(
|
|||
.send()
|
||||
.await
|
||||
.map_err(server_client::map_api_error)?;
|
||||
if globals.json {
|
||||
if cli.output.format == OutputFormat::Json {
|
||||
print_json_pretty(&serde_json::json!({ "key": args.key }))?;
|
||||
} else {
|
||||
fabro_util::printerr!(printer, "Removed {}", args.key);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
use anyhow::Result;
|
||||
use fabro_api::{Client, types};
|
||||
use fabro_types::settings::CliSettings;
|
||||
use fabro_types::settings::cli::OutputFormat;
|
||||
use fabro_util::printer::Printer;
|
||||
|
||||
use crate::args::{GlobalArgs, SecretSetArgs, SecretTypeArg};
|
||||
use crate::args::{SecretSetArgs, SecretTypeArg};
|
||||
use crate::server_client;
|
||||
use crate::shared::print_json_pretty;
|
||||
|
||||
|
|
@ -16,7 +18,7 @@ fn api_secret_type(secret_type: SecretTypeArg) -> types::SecretType {
|
|||
pub(super) async fn set_command(
|
||||
client: &Client,
|
||||
args: &SecretSetArgs,
|
||||
globals: &GlobalArgs,
|
||||
cli: &CliSettings,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
let meta = client
|
||||
|
|
@ -31,7 +33,7 @@ pub(super) async fn set_command(
|
|||
.await
|
||||
.map_err(server_client::map_api_error)?
|
||||
.into_inner();
|
||||
if globals.json {
|
||||
if cli.output.format == OutputFormat::Json {
|
||||
print_json_pretty(&meta)?;
|
||||
} else {
|
||||
fabro_util::printerr!(printer, "Set {}", meta.name);
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ use bytes::Bytes;
|
|||
#[cfg(test)]
|
||||
use fabro_store::{ArtifactStore, RunDatabase};
|
||||
use fabro_store::{EventEnvelope, RunProjection, StageId};
|
||||
use fabro_types::settings::CliSettings;
|
||||
use fabro_types::settings::cli::OutputFormat;
|
||||
use fabro_types::{RunBlobId, RunId};
|
||||
use fabro_util::printer::Printer;
|
||||
use fabro_workflow::run_dump::RunDump;
|
||||
|
|
@ -13,7 +15,7 @@ use futures::future::BoxFuture;
|
|||
#[cfg(test)]
|
||||
use serde::de::DeserializeOwned;
|
||||
|
||||
use crate::args::{GlobalArgs, StoreDumpArgs};
|
||||
use crate::args::StoreDumpArgs;
|
||||
use crate::server_client::ServerStoreClient;
|
||||
use crate::server_runs::ServerRunLookup;
|
||||
use crate::shared::{absolute_or_current, print_json_pretty};
|
||||
|
|
@ -21,7 +23,7 @@ use crate::user_config::{load_settings_with_storage_dir, storage_dir};
|
|||
|
||||
pub(crate) async fn dump_command(
|
||||
args: &StoreDumpArgs,
|
||||
globals: &GlobalArgs,
|
||||
cli: &CliSettings,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
let cli_settings = load_settings_with_storage_dir(args.storage_dir.as_deref())?;
|
||||
|
|
@ -31,7 +33,7 @@ pub(crate) async fn dump_command(
|
|||
let state = lookup.client().get_run_state(&run_id).await?;
|
||||
let source = ServerDumpSource::new(lookup.client(), &run_id);
|
||||
let file_count = export_run_from_source(&source, &state, &args.output).await?;
|
||||
if globals.json {
|
||||
if cli.output.format == OutputFormat::Json {
|
||||
print_json_pretty(&serde_json::json!({
|
||||
"run_id": run_id,
|
||||
"output_dir": absolute_or_current(&args.output),
|
||||
|
|
|
|||
|
|
@ -2,16 +2,17 @@ pub(crate) mod dump;
|
|||
pub(crate) mod rebuild;
|
||||
|
||||
use anyhow::Result;
|
||||
use fabro_types::settings::CliSettings;
|
||||
use fabro_util::printer::Printer;
|
||||
|
||||
use crate::args::{GlobalArgs, StoreCommand, StoreNamespace};
|
||||
use crate::args::{StoreCommand, StoreNamespace};
|
||||
|
||||
pub(crate) async fn dispatch(
|
||||
ns: StoreNamespace,
|
||||
globals: &GlobalArgs,
|
||||
cli: &CliSettings,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
match ns.command {
|
||||
StoreCommand::Dump(args) => dump::dump_command(&args, globals, printer).await,
|
||||
StoreCommand::Dump(args) => dump::dump_command(&args, cli, printer).await,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,19 +3,22 @@ use chrono::{DateTime, Utc};
|
|||
use cli_table::format::{Border, Justify, Separator};
|
||||
use cli_table::{Cell, CellStruct, Style, Table};
|
||||
use fabro_api::types;
|
||||
use fabro_types::settings::CliSettings;
|
||||
use fabro_types::settings::cli::{CliLayer, OutputFormat};
|
||||
use fabro_util::printer::Printer;
|
||||
|
||||
use crate::args::{DfArgs, GlobalArgs};
|
||||
use crate::args::DfArgs;
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::server_client;
|
||||
use crate::shared::{format_size, print_json_pretty};
|
||||
|
||||
pub(super) async fn df_command(
|
||||
args: &DfArgs,
|
||||
globals: &GlobalArgs,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
let ctx = CommandContext::for_connection(&args.connection, printer)?;
|
||||
let ctx = CommandContext::for_connection(&args.connection, printer, cli.clone(), cli_layer)?;
|
||||
let server = ctx.server().await?;
|
||||
let output = server
|
||||
.api()
|
||||
|
|
@ -26,7 +29,8 @@ pub(super) async fn df_command(
|
|||
.map_err(server_client::map_api_error)?
|
||||
.into_inner();
|
||||
|
||||
let storage_dir = if globals.json {
|
||||
let json = cli.output.format == OutputFormat::Json;
|
||||
let storage_dir = if json {
|
||||
None
|
||||
} else {
|
||||
server
|
||||
|
|
@ -39,14 +43,14 @@ pub(super) async fn df_command(
|
|||
.storage_dir
|
||||
};
|
||||
|
||||
df_from(&output, storage_dir.as_deref(), globals)
|
||||
df_from(&output, storage_dir.as_deref(), json)
|
||||
}
|
||||
|
||||
#[allow(clippy::print_stdout)]
|
||||
fn df_from(
|
||||
output: &types::DiskUsageResponse,
|
||||
storage_dir: Option<&str>,
|
||||
globals: &GlobalArgs,
|
||||
json_output: bool,
|
||||
) -> Result<()> {
|
||||
let runs_summary = output
|
||||
.summary
|
||||
|
|
@ -82,7 +86,7 @@ fn df_from(
|
|||
};
|
||||
let log_reclaim_pct = if total_log_size > 0 { 100 } else { 0 };
|
||||
|
||||
if globals.json {
|
||||
if json_output {
|
||||
print_json_pretty(output)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,20 @@
|
|||
use anyhow::Result;
|
||||
use fabro_types::settings::CliSettings;
|
||||
use fabro_types::settings::cli::{CliLayer, OutputFormat};
|
||||
use fabro_util::printer::Printer;
|
||||
use futures::StreamExt;
|
||||
|
||||
use crate::args::{GlobalArgs, SystemEventsArgs};
|
||||
use crate::args::SystemEventsArgs;
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::{server_client, sse};
|
||||
|
||||
pub(super) async fn events_command(
|
||||
args: &SystemEventsArgs,
|
||||
globals: &GlobalArgs,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
let ctx = CommandContext::for_connection(&args.connection, printer)?;
|
||||
let ctx = CommandContext::for_connection(&args.connection, printer, cli.clone(), cli_layer)?;
|
||||
let server = ctx.server().await?;
|
||||
|
||||
let mut request = server.api().attach_events();
|
||||
|
|
@ -23,16 +26,17 @@ pub(super) async fn events_command(
|
|||
let mut stream = response.into_inner();
|
||||
let mut pending = Vec::new();
|
||||
|
||||
let json = cli.output.format == OutputFormat::Json;
|
||||
while let Some(chunk) = stream.next().await {
|
||||
let chunk = chunk.map_err(|err| anyhow::anyhow!("{err}"))?;
|
||||
pending.extend_from_slice(&chunk);
|
||||
for payload in sse::drain_sse_payloads(&mut pending, false) {
|
||||
render_sse_payload(&payload, globals.json)?;
|
||||
render_sse_payload(&payload, json)?;
|
||||
}
|
||||
}
|
||||
|
||||
for payload in sse::drain_sse_payloads(&mut pending, true) {
|
||||
render_sse_payload(&payload, globals.json)?;
|
||||
render_sse_payload(&payload, json)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -1,17 +1,20 @@
|
|||
use anyhow::Result;
|
||||
use fabro_types::settings::CliSettings;
|
||||
use fabro_types::settings::cli::{CliLayer, OutputFormat};
|
||||
use fabro_util::printer::Printer;
|
||||
|
||||
use crate::args::{GlobalArgs, SystemInfoArgs};
|
||||
use crate::args::SystemInfoArgs;
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::server_client;
|
||||
use crate::shared::print_json_pretty;
|
||||
|
||||
pub(super) async fn info_command(
|
||||
args: &SystemInfoArgs,
|
||||
globals: &GlobalArgs,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
let ctx = CommandContext::for_connection(&args.connection, printer)?;
|
||||
let ctx = CommandContext::for_connection(&args.connection, printer, cli.clone(), cli_layer)?;
|
||||
let server = ctx.server().await?;
|
||||
let response = server
|
||||
.api()
|
||||
|
|
@ -21,7 +24,7 @@ pub(super) async fn info_command(
|
|||
.map_err(server_client::map_api_error)?
|
||||
.into_inner();
|
||||
|
||||
if globals.json {
|
||||
if cli.output.format == OutputFormat::Json {
|
||||
print_json_pretty(&response)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,20 +4,23 @@ mod info;
|
|||
mod prune;
|
||||
|
||||
use anyhow::Result;
|
||||
use fabro_types::settings::CliSettings;
|
||||
use fabro_types::settings::cli::CliLayer;
|
||||
use fabro_util::printer::Printer;
|
||||
pub(crate) use prune::parse_duration;
|
||||
|
||||
use crate::args::{GlobalArgs, SystemCommand, SystemNamespace};
|
||||
use crate::args::{SystemCommand, SystemNamespace};
|
||||
|
||||
pub(crate) async fn dispatch(
|
||||
ns: SystemNamespace,
|
||||
globals: &GlobalArgs,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
match ns.command {
|
||||
SystemCommand::Info(args) => info::info_command(&args, globals, printer).await,
|
||||
SystemCommand::Prune(args) => prune::prune_command(&args, globals, printer).await,
|
||||
SystemCommand::Df(args) => df::df_command(&args, globals, printer).await,
|
||||
SystemCommand::Events(args) => events::events_command(&args, globals, printer).await,
|
||||
SystemCommand::Info(args) => info::info_command(&args, cli, cli_layer, printer).await,
|
||||
SystemCommand::Prune(args) => prune::prune_command(&args, cli, cli_layer, printer).await,
|
||||
SystemCommand::Df(args) => df::df_command(&args, cli, cli_layer, printer).await,
|
||||
SystemCommand::Events(args) => events::events_command(&args, cli, cli_layer, printer).await,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,20 +2,23 @@ use std::collections::HashMap;
|
|||
|
||||
use anyhow::{Context, Result, bail};
|
||||
use fabro_api::types;
|
||||
use fabro_types::settings::CliSettings;
|
||||
use fabro_types::settings::cli::{CliLayer, OutputFormat};
|
||||
use fabro_util::printer::Printer;
|
||||
use tracing::{debug, info};
|
||||
|
||||
use crate::args::{GlobalArgs, RunsPruneArgs};
|
||||
use crate::args::RunsPruneArgs;
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::server_client;
|
||||
use crate::shared::{format_size, print_json_pretty};
|
||||
|
||||
pub(super) async fn prune_command(
|
||||
args: &RunsPruneArgs,
|
||||
globals: &GlobalArgs,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
let ctx = CommandContext::for_connection(&args.connection, printer)?;
|
||||
let ctx = CommandContext::for_connection(&args.connection, printer, cli.clone(), cli_layer)?;
|
||||
let server = ctx.server().await?;
|
||||
let response = server
|
||||
.api()
|
||||
|
|
@ -32,7 +35,7 @@ pub(super) async fn prune_command(
|
|||
.await
|
||||
.map_err(server_client::map_api_error)?
|
||||
.into_inner();
|
||||
prune_from(&response, globals, printer)
|
||||
prune_from(&response, cli.output.format == OutputFormat::Json, printer)
|
||||
}
|
||||
|
||||
pub(crate) fn parse_duration(s: &str) -> Result<chrono::Duration> {
|
||||
|
|
@ -53,7 +56,7 @@ pub(crate) fn parse_duration(s: &str) -> Result<chrono::Duration> {
|
|||
|
||||
fn prune_from(
|
||||
response: &types::PruneRunsResponse,
|
||||
globals: &GlobalArgs,
|
||||
json_output: bool,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
let total_count = response.total_count.unwrap_or_default();
|
||||
|
|
@ -66,7 +69,7 @@ fn prune_from(
|
|||
"pruning runs"
|
||||
);
|
||||
|
||||
if globals.json {
|
||||
if json_output {
|
||||
print_json_pretty(response)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,12 +4,14 @@ use std::path::{Path, PathBuf};
|
|||
use std::time::Duration;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use fabro_types::settings::CliSettings;
|
||||
use fabro_types::settings::cli::OutputFormat;
|
||||
use fabro_util::Home;
|
||||
use fabro_util::printer::Printer;
|
||||
use serde::Serialize;
|
||||
use tracing::warn;
|
||||
|
||||
use crate::args::{GlobalArgs, UninstallArgs};
|
||||
use crate::args::UninstallArgs;
|
||||
use crate::commands::server;
|
||||
use crate::shared::{format_size, print_json_pretty, tilde_path};
|
||||
use crate::user_config;
|
||||
|
|
@ -29,14 +31,15 @@ struct Inventory {
|
|||
#[allow(clippy::unused_async)] // call site requires async
|
||||
pub(crate) async fn run_uninstall(
|
||||
args: &UninstallArgs,
|
||||
globals: &GlobalArgs,
|
||||
cli: &CliSettings,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
let json = cli.output.format == OutputFormat::Json;
|
||||
let home = Home::from_env();
|
||||
let home_root = home.root().to_path_buf();
|
||||
|
||||
if !looks_like_fabro_home(&home_root) {
|
||||
if globals.json {
|
||||
if json {
|
||||
print_json_pretty(&serde_json::json!({ "status": "not_installed" }))?;
|
||||
} else {
|
||||
fabro_util::printerr!(printer, "Fabro is not installed.");
|
||||
|
|
@ -52,7 +55,7 @@ pub(crate) async fn run_uninstall(
|
|||
let inventory = build_inventory(&home_root, &storage_dir);
|
||||
|
||||
if !args.yes {
|
||||
if globals.json {
|
||||
if json {
|
||||
print_json_pretty(&inventory)?;
|
||||
} else {
|
||||
print_preview(&inventory, printer);
|
||||
|
|
@ -60,7 +63,7 @@ pub(crate) async fn run_uninstall(
|
|||
return Ok(());
|
||||
}
|
||||
|
||||
execute_uninstall(&inventory, globals.json, printer).await
|
||||
execute_uninstall(&inventory, json, printer).await
|
||||
}
|
||||
|
||||
fn build_inventory(home_root: &Path, storage_dir: &Path) -> Inventory {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ use std::io::{IsTerminal, Write};
|
|||
use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::{Context, Result, bail};
|
||||
use fabro_types::settings::CliSettings;
|
||||
use fabro_types::settings::cli::OutputFormat;
|
||||
use fabro_util::printer::Printer;
|
||||
use semver::Version;
|
||||
use sha2::{Digest, Sha256};
|
||||
|
|
@ -10,7 +12,7 @@ use tokio::process::Command as TokioCommand;
|
|||
use tokio::task::JoinHandle;
|
||||
use tracing::debug;
|
||||
|
||||
use crate::args::{GlobalArgs, UpgradeArgs};
|
||||
use crate::args::UpgradeArgs;
|
||||
use crate::shared::print_json_pretty;
|
||||
|
||||
// ── Download backend abstraction ───────────────────────────────────────────
|
||||
|
|
@ -226,7 +228,7 @@ impl UpgradeCheckState {
|
|||
|
||||
pub(crate) async fn run_upgrade(
|
||||
args: UpgradeArgs,
|
||||
globals: &GlobalArgs,
|
||||
cli: &CliSettings,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
let backend = select_backend().await;
|
||||
|
|
@ -268,7 +270,7 @@ pub(crate) async fn run_upgrade(
|
|||
}
|
||||
}
|
||||
std::cmp::Ordering::Equal if !args.force => {
|
||||
if globals.json {
|
||||
if cli.output.format == OutputFormat::Json {
|
||||
print_json_pretty(&serde_json::json!({
|
||||
"previous_version": current.to_string(),
|
||||
"installed_version": current.to_string(),
|
||||
|
|
@ -282,7 +284,7 @@ pub(crate) async fn run_upgrade(
|
|||
}
|
||||
|
||||
if args.dry_run {
|
||||
if globals.json {
|
||||
if cli.output.format == OutputFormat::Json {
|
||||
print_json_pretty(&serde_json::json!({
|
||||
"previous_version": current.to_string(),
|
||||
"installed_version": target.to_string(),
|
||||
|
|
@ -358,7 +360,7 @@ pub(crate) async fn run_upgrade(
|
|||
let _ = fs::set_permissions(¤t_exe, fs::Permissions::from_mode(0o755));
|
||||
}
|
||||
|
||||
if globals.json {
|
||||
if cli.output.format == OutputFormat::Json {
|
||||
print_json_pretty(&serde_json::json!({
|
||||
"previous_version": current.to_string(),
|
||||
"installed_version": target.to_string(),
|
||||
|
|
@ -374,12 +376,8 @@ pub(crate) async fn run_upgrade(
|
|||
/// Spawn a background task that checks for a newer version and prints a notice
|
||||
/// to stderr after the main command completes. Returns a handle that should be
|
||||
/// awaited at the end of `main_inner`.
|
||||
pub(crate) fn spawn_upgrade_check(
|
||||
no_upgrade_check: bool,
|
||||
upgrade_check_enabled: bool,
|
||||
printer: Printer,
|
||||
) -> Option<JoinHandle<()>> {
|
||||
if no_upgrade_check || !upgrade_check_enabled {
|
||||
pub(crate) fn spawn_upgrade_check(check: bool, printer: Printer) -> Option<JoinHandle<()>> {
|
||||
if !check {
|
||||
return None;
|
||||
}
|
||||
Some(tokio::spawn(async move {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
use anyhow::bail;
|
||||
use fabro_config::load::load_settings_user;
|
||||
use fabro_config::user::active_settings_path;
|
||||
use fabro_types::settings::SettingsLayer;
|
||||
use fabro_types::settings::cli::{CliLayer, OutputFormat};
|
||||
use fabro_types::settings::{CliSettings, SettingsLayer};
|
||||
use fabro_util::printer::Printer;
|
||||
use fabro_util::terminal::Styles;
|
||||
|
||||
use crate::args::{GlobalArgs, ValidateArgs};
|
||||
use crate::args::ValidateArgs;
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::commands::run::output::api_diagnostics_to_local;
|
||||
use crate::manifest_builder::{ManifestBuildInput, build_run_manifest};
|
||||
|
|
@ -14,10 +15,11 @@ use crate::shared::{print_diagnostics, print_json_pretty, relative_path};
|
|||
pub(crate) async fn run(
|
||||
args: &ValidateArgs,
|
||||
styles: &Styles,
|
||||
globals: &GlobalArgs,
|
||||
cli: &CliSettings,
|
||||
cli_layer: &CliLayer,
|
||||
printer: Printer,
|
||||
) -> anyhow::Result<()> {
|
||||
let ctx = CommandContext::for_target(&args.target, printer)?;
|
||||
let ctx = CommandContext::for_target(&args.target, printer, cli.clone(), cli_layer)?;
|
||||
let built = build_run_manifest(ManifestBuildInput {
|
||||
workflow: args.workflow.clone(),
|
||||
cwd: ctx.cwd().to_path_buf(),
|
||||
|
|
@ -31,7 +33,7 @@ pub(crate) async fn run(
|
|||
let response = client.run_preflight(built.manifest).await?;
|
||||
let diagnostics = api_diagnostics_to_local(&response.workflow.diagnostics);
|
||||
|
||||
if globals.json {
|
||||
if cli.output.format == OutputFormat::Json {
|
||||
print_json_pretty(&serde_json::json!({
|
||||
"workflow_name": response.workflow.name,
|
||||
"nodes": response.workflow.nodes,
|
||||
|
|
|
|||
|
|
@ -2,14 +2,16 @@ use std::path::Path;
|
|||
|
||||
use anyhow::{Context, Result, bail};
|
||||
use fabro_config::project::{discover_project_config, resolve_fabro_root};
|
||||
use fabro_types::settings::CliSettings;
|
||||
use fabro_types::settings::cli::OutputFormat;
|
||||
use fabro_util::printer::Printer;
|
||||
|
||||
use crate::args::{GlobalArgs, WorkflowCreateArgs};
|
||||
use crate::args::WorkflowCreateArgs;
|
||||
use crate::shared::{print_json_pretty, relative_path};
|
||||
|
||||
pub(super) fn create_command(
|
||||
args: &WorkflowCreateArgs,
|
||||
globals: &GlobalArgs,
|
||||
cli: &CliSettings,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
let cwd = std::env::current_dir()?;
|
||||
|
|
@ -24,7 +26,7 @@ pub(super) fn create_command(
|
|||
let fabro_root = resolve_fabro_root(&config_path, &config);
|
||||
let created = write_workflow_scaffold(args, &fabro_root)?;
|
||||
|
||||
if globals.json {
|
||||
if cli.output.format == OutputFormat::Json {
|
||||
let created: Vec<_> = created.iter().map(|path| relative_path(path)).collect();
|
||||
print_json_pretty(&serde_json::json!({
|
||||
"name": args.name,
|
||||
|
|
|
|||
|
|
@ -5,17 +5,19 @@ use fabro_config::project::{
|
|||
WorkflowInfo, WorkflowSource, discover_project_config, list_workflows_detailed,
|
||||
resolve_fabro_root,
|
||||
};
|
||||
use fabro_types::settings::CliSettings;
|
||||
use fabro_types::settings::cli::OutputFormat;
|
||||
use fabro_util::printer::Printer;
|
||||
use fabro_util::terminal::Styles;
|
||||
|
||||
use crate::args::{GlobalArgs, WorkflowListArgs};
|
||||
use crate::args::WorkflowListArgs;
|
||||
use crate::shared::{color_if, print_json_pretty, relative_path};
|
||||
|
||||
const GOAL_MAX_LEN: usize = 60;
|
||||
|
||||
pub(super) fn list_command(
|
||||
_args: &WorkflowListArgs,
|
||||
globals: &GlobalArgs,
|
||||
cli: &CliSettings,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
let styles = Styles::detect_stderr();
|
||||
|
|
@ -34,7 +36,7 @@ pub(super) fn list_command(
|
|||
|
||||
let workflows = list_workflows_detailed(Some(&project_wf_dir), user_wf_dir.as_deref());
|
||||
|
||||
if globals.json {
|
||||
if cli.output.format == OutputFormat::Json {
|
||||
print_json_pretty(&workflows)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,17 +2,14 @@ mod create;
|
|||
mod list;
|
||||
|
||||
use anyhow::Result;
|
||||
use fabro_types::settings::CliSettings;
|
||||
use fabro_util::printer::Printer;
|
||||
|
||||
use crate::args::{GlobalArgs, WorkflowCommand, WorkflowNamespace};
|
||||
use crate::args::{WorkflowCommand, WorkflowNamespace};
|
||||
|
||||
pub(crate) fn dispatch(
|
||||
ns: WorkflowNamespace,
|
||||
globals: &GlobalArgs,
|
||||
printer: Printer,
|
||||
) -> Result<()> {
|
||||
pub(crate) fn dispatch(ns: WorkflowNamespace, cli: &CliSettings, printer: Printer) -> Result<()> {
|
||||
match ns.command {
|
||||
WorkflowCommand::List(args) => list::list_command(&args, globals, printer),
|
||||
WorkflowCommand::Create(args) => create::create_command(&args, globals, printer),
|
||||
WorkflowCommand::List(args) => list::list_command(&args, cli, printer),
|
||||
WorkflowCommand::Create(args) => create::create_command(&args, cli, printer),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,10 +18,15 @@ mod user_config;
|
|||
use std::ffi::OsString;
|
||||
|
||||
use anyhow::Result;
|
||||
use args::{Commands, GlobalArgs, LONG_VERSION, RunCommands, ServerCommand, ServerNamespace};
|
||||
use args::{
|
||||
Commands, GlobalArgs, LONG_VERSION, RunCommands, ServerCommand, ServerNamespace,
|
||||
global_args_cli_layer, printer_from_verbosity, require_no_json_override,
|
||||
};
|
||||
use clap::{CommandFactory, Parser};
|
||||
use fabro_config::merge::combine_files;
|
||||
use fabro_config::user::load_settings_config;
|
||||
use fabro_telemetry::{git, panic as tel_panic, sanitize, sender};
|
||||
use fabro_types::settings::SettingsLayer;
|
||||
use fabro_types::settings::cli::OutputVerbosity;
|
||||
use fabro_util::printer::Printer;
|
||||
use fabro_util::terminal::Styles;
|
||||
|
|
@ -120,43 +125,45 @@ async fn main_inner() -> (String, Result<()>) {
|
|||
let cli = Cli::parse();
|
||||
|
||||
let Cli { globals, command } = cli;
|
||||
let printer = Printer::from_flags(globals.quiet, globals.verbose);
|
||||
let bootstrap_printer = Printer::from_flags(globals.quiet, globals.verbose);
|
||||
let cli_layer = global_args_cli_layer(&globals);
|
||||
let process_local_json = globals.json;
|
||||
let command_name = command.name().to_string();
|
||||
|
||||
let (config_log_level, upgrade_check_enabled) = {
|
||||
if let Commands::Server(ServerNamespace {
|
||||
command:
|
||||
ServerCommand::Start(args::ServerStartArgs {
|
||||
serve_args: args, ..
|
||||
})
|
||||
| ServerCommand::Serve(args::ServerServeArgs {
|
||||
serve_args: args, ..
|
||||
}),
|
||||
}) = command.as_ref()
|
||||
{
|
||||
match load_settings_config(args.config.as_deref()) {
|
||||
Ok(layer) => {
|
||||
let server_settings = layer;
|
||||
(
|
||||
server_settings
|
||||
.server
|
||||
.as_ref()
|
||||
.and_then(|server| server.logging.as_ref())
|
||||
.and_then(|logging| logging.level.clone()),
|
||||
false,
|
||||
)
|
||||
}
|
||||
Err(err) => return (command_name, Err(err.into())),
|
||||
}
|
||||
} else {
|
||||
match user_config::load_settings() {
|
||||
Ok(cli_settings) => match user_config::resolve_cli_settings(&cli_settings) {
|
||||
Ok(resolved_cli) => (resolved_cli.logging.level, resolved_cli.updates.check),
|
||||
Err(err) => return (command_name, Err(err)),
|
||||
},
|
||||
Err(err) => return (command_name, Err(err)),
|
||||
}
|
||||
let user_settings = match user_config::load_settings() {
|
||||
Ok(settings) => settings,
|
||||
Err(err) => return (command_name, Err(err)),
|
||||
};
|
||||
let combined_settings = combine_files(user_settings, SettingsLayer {
|
||||
cli: Some(cli_layer.clone()),
|
||||
..SettingsLayer::default()
|
||||
});
|
||||
let cli_settings = match user_config::resolve_cli_settings(&combined_settings) {
|
||||
Ok(cli_settings) => cli_settings,
|
||||
Err(err) => return (command_name, Err(err)),
|
||||
};
|
||||
let printer = printer_from_verbosity(cli_settings.output.verbosity);
|
||||
|
||||
let config_log_level = if let Commands::Server(ServerNamespace {
|
||||
command:
|
||||
ServerCommand::Start(args::ServerStartArgs {
|
||||
serve_args: args, ..
|
||||
})
|
||||
| ServerCommand::Serve(args::ServerServeArgs {
|
||||
serve_args: args, ..
|
||||
}),
|
||||
}) = command.as_ref()
|
||||
{
|
||||
match load_settings_config(args.config.as_deref()) {
|
||||
Ok(layer) => layer
|
||||
.server
|
||||
.as_ref()
|
||||
.and_then(|server| server.logging.as_ref())
|
||||
.and_then(|logging| logging.level.clone()),
|
||||
Err(err) => return (command_name, Err(err.into())),
|
||||
}
|
||||
} else {
|
||||
cli_settings.logging.level.clone()
|
||||
};
|
||||
|
||||
let log_prefix = if command_name == "server start" || command_name == "server __serve" {
|
||||
|
|
@ -166,7 +173,10 @@ async fn main_inner() -> (String, Result<()>) {
|
|||
};
|
||||
if let Err(err) = logging::init_tracing(globals.debug, config_log_level.as_deref(), log_prefix)
|
||||
{
|
||||
fabro_util::printerr!(printer, "Warning: failed to initialize logging: {err:#}");
|
||||
fabro_util::printerr!(
|
||||
bootstrap_printer,
|
||||
"Warning: failed to initialize logging: {err:#}"
|
||||
);
|
||||
}
|
||||
|
||||
debug!(command = %command_name, "CLI command started");
|
||||
|
|
@ -178,59 +188,74 @@ async fn main_inner() -> (String, Result<()>) {
|
|||
| Commands::Repo(_)
|
||||
| Commands::Install(_)
|
||||
) {
|
||||
commands::upgrade::spawn_upgrade_check(
|
||||
globals.no_upgrade_check,
|
||||
upgrade_check_enabled,
|
||||
printer,
|
||||
)
|
||||
commands::upgrade::spawn_upgrade_check(cli_settings.updates.check, printer)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let result = Box::pin(async move {
|
||||
match *command {
|
||||
Commands::Exec(args) => commands::exec::execute(args, &globals, printer).await?,
|
||||
Commands::Exec(args) => commands::exec::execute(args, &cli_settings, printer).await?,
|
||||
Commands::RunCmd(cmd) => {
|
||||
Box::pin(commands::run::dispatch(cmd, &globals, printer)).await?;
|
||||
Box::pin(commands::run::dispatch(
|
||||
cmd,
|
||||
&cli_settings,
|
||||
&cli_layer,
|
||||
process_local_json,
|
||||
printer,
|
||||
))
|
||||
.await?;
|
||||
}
|
||||
Commands::Preflight(args) => {
|
||||
commands::preflight::execute(args, &globals, printer).await?;
|
||||
commands::preflight::execute(args, &cli_settings, &cli_layer, printer).await?;
|
||||
}
|
||||
Commands::Validate(args) => {
|
||||
let styles = Styles::detect_stderr();
|
||||
commands::validate::run(&args, &styles, &globals, printer).await?;
|
||||
commands::validate::run(&args, &styles, &cli_settings, &cli_layer, printer).await?;
|
||||
}
|
||||
Commands::Graph(args) => {
|
||||
let styles = Styles::detect_stderr();
|
||||
commands::graph::run(&args, &styles, &globals, printer).await?;
|
||||
commands::graph::run(
|
||||
&args,
|
||||
&styles,
|
||||
&cli_settings,
|
||||
&cli_layer,
|
||||
process_local_json,
|
||||
printer,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
Commands::Parse(args) => {
|
||||
commands::parse::run(&args, &globals, printer)?;
|
||||
commands::parse::run(&args, &cli_settings, printer)?;
|
||||
}
|
||||
Commands::Artifact(ns) => {
|
||||
commands::artifact::dispatch(ns, &cli_settings, &cli_layer, printer).await?;
|
||||
}
|
||||
Commands::Store(ns) => commands::store::dispatch(ns, &cli_settings, printer).await?,
|
||||
Commands::RunsCmd(cmd) => {
|
||||
commands::runs::dispatch(cmd, &cli_settings, &cli_layer, printer).await?;
|
||||
}
|
||||
Commands::Artifact(ns) => commands::artifact::dispatch(ns, &globals, printer).await?,
|
||||
Commands::Store(ns) => commands::store::dispatch(ns, &globals, printer).await?,
|
||||
Commands::RunsCmd(cmd) => commands::runs::dispatch(cmd, &globals, printer).await?,
|
||||
Commands::Model { command } => {
|
||||
commands::model::execute(command, &globals, printer).await?;
|
||||
commands::model::execute(command, &cli_settings, &cli_layer, printer).await?;
|
||||
}
|
||||
Commands::Server(ns) => {
|
||||
Box::pin(commands::server::dispatch(ns.command, &globals, printer)).await?;
|
||||
}
|
||||
Commands::Doctor(args) => {
|
||||
let cli_settings = user_config::load_settings()?;
|
||||
let verbose = args.verbose
|
||||
|| user_config::resolve_cli_settings(&cli_settings)?
|
||||
.output
|
||||
.verbosity
|
||||
== OutputVerbosity::Verbose;
|
||||
let verbose =
|
||||
args.verbose || cli_settings.output.verbosity == OutputVerbosity::Verbose;
|
||||
let exit_code = Box::pin(commands::doctor::run_doctor(
|
||||
&args, verbose, &globals, printer,
|
||||
&args,
|
||||
verbose,
|
||||
&cli_settings,
|
||||
&cli_layer,
|
||||
printer,
|
||||
))
|
||||
.await?;
|
||||
std::process::exit(exit_code);
|
||||
}
|
||||
Commands::Discord => {
|
||||
if globals.json {
|
||||
if process_local_json {
|
||||
shared::print_json_pretty(&serde_json::json!({
|
||||
"url": "https://fabro.sh/discord",
|
||||
}))?;
|
||||
|
|
@ -239,7 +264,7 @@ async fn main_inner() -> (String, Result<()>) {
|
|||
}
|
||||
}
|
||||
Commands::Docs => {
|
||||
if globals.json {
|
||||
if process_local_json {
|
||||
shared::print_json_pretty(&serde_json::json!({
|
||||
"url": "https://docs.fabro.sh/",
|
||||
}))?;
|
||||
|
|
@ -247,29 +272,72 @@ async fn main_inner() -> (String, Result<()>) {
|
|||
open::that("https://docs.fabro.sh/")?;
|
||||
}
|
||||
}
|
||||
Commands::Repo(ns) => commands::repo::dispatch(ns, &globals, printer).await?,
|
||||
Commands::Repo(ns) => {
|
||||
commands::repo::dispatch(ns, &cli_settings, &cli_layer, printer).await?;
|
||||
}
|
||||
Commands::Install(args) => {
|
||||
Box::pin(commands::install::run_install(&args, &globals, printer)).await?;
|
||||
Box::pin(commands::install::run_install(
|
||||
&args,
|
||||
&cli_settings,
|
||||
&cli_layer,
|
||||
process_local_json,
|
||||
printer,
|
||||
))
|
||||
.await?;
|
||||
}
|
||||
Commands::Uninstall(args) => {
|
||||
commands::uninstall::run_uninstall(&args, &globals, printer).await?;
|
||||
commands::uninstall::run_uninstall(&args, &cli_settings, printer).await?;
|
||||
}
|
||||
Commands::Pr(ns) => {
|
||||
Box::pin(commands::pr::dispatch(
|
||||
ns,
|
||||
&cli_settings,
|
||||
&cli_layer,
|
||||
printer,
|
||||
))
|
||||
.await?;
|
||||
}
|
||||
Commands::Secret(ns) => {
|
||||
commands::secret::dispatch(ns, &cli_settings, &cli_layer, printer).await?;
|
||||
}
|
||||
Commands::Pr(ns) => Box::pin(commands::pr::dispatch(ns, &globals, printer)).await?,
|
||||
Commands::Secret(ns) => commands::secret::dispatch(ns, &globals, printer).await?,
|
||||
Commands::Settings(args) => {
|
||||
Box::pin(commands::config::execute(&args, &globals, printer)).await?;
|
||||
Box::pin(commands::config::execute(
|
||||
&args,
|
||||
&cli_settings,
|
||||
&cli_layer,
|
||||
printer,
|
||||
))
|
||||
.await?;
|
||||
}
|
||||
Commands::Workflow(ns) => commands::workflow::dispatch(ns, &globals, printer)?,
|
||||
Commands::Workflow(ns) => commands::workflow::dispatch(ns, &cli_settings, printer)?,
|
||||
Commands::Upgrade(args) => {
|
||||
commands::upgrade::run_upgrade(args, &globals, printer).await?;
|
||||
commands::upgrade::run_upgrade(args, &cli_settings, printer).await?;
|
||||
}
|
||||
Commands::Provider(ns) => {
|
||||
commands::provider::dispatch(
|
||||
ns,
|
||||
&cli_settings,
|
||||
&cli_layer,
|
||||
process_local_json,
|
||||
printer,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
Commands::Provider(ns) => commands::provider::dispatch(ns, &globals, printer).await?,
|
||||
Commands::Sandbox { command } => {
|
||||
commands::sandbox::dispatch(command, &globals, printer).await?;
|
||||
commands::sandbox::dispatch(
|
||||
command,
|
||||
&cli_settings,
|
||||
&cli_layer,
|
||||
process_local_json,
|
||||
printer,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
Commands::System(ns) => {
|
||||
commands::system::dispatch(ns, &cli_settings, &cli_layer, printer).await?;
|
||||
}
|
||||
Commands::System(ns) => commands::system::dispatch(ns, &globals, printer).await?,
|
||||
Commands::Completion(args) => {
|
||||
globals.require_no_json()?;
|
||||
require_no_json_override(process_local_json)?;
|
||||
let mut cmd = Cli::command();
|
||||
let shell = args.shell;
|
||||
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
|
||||
|
|
|
|||
|
|
@ -409,7 +409,7 @@ fn create_persists_requested_overrides_into_store() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn create_json_implies_auto_approve() {
|
||||
fn create_json_does_not_imply_auto_approve() {
|
||||
let context = test_context!();
|
||||
let workflow = fixture("simple.fabro");
|
||||
let output = context
|
||||
|
|
@ -442,7 +442,7 @@ fn create_json_implies_auto_approve() {
|
|||
)
|
||||
.execution
|
||||
.approval
|
||||
== fabro_types::settings::run::ApprovalMode::Auto
|
||||
!= fabro_types::settings::run::ApprovalMode::Auto
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,62 @@ fn settings_json_outputs_parseable_json() {
|
|||
assert!(value.is_object());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn settings_uses_json_output_format_from_home_config() {
|
||||
let context = test_context!();
|
||||
context.write_home(
|
||||
".fabro/settings.toml",
|
||||
"_version = 1\n\n[cli.output]\nformat = \"json\"\n",
|
||||
);
|
||||
|
||||
let output = context.settings().output().expect("command should run");
|
||||
|
||||
assert!(output.status.success());
|
||||
let value: Value =
|
||||
serde_json::from_slice(&output.stdout).expect("settings config JSON should parse");
|
||||
assert!(value.is_object());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn secret_list_uses_json_output_format_from_home_config() {
|
||||
let context = test_context!();
|
||||
context.write_home(
|
||||
".fabro/settings.toml",
|
||||
"_version = 1\n\n[cli.output]\nformat = \"json\"\n",
|
||||
);
|
||||
|
||||
let output = context
|
||||
.command()
|
||||
.args(["secret", "list"])
|
||||
.output()
|
||||
.expect("command should run");
|
||||
|
||||
assert!(output.status.success());
|
||||
let value: Value =
|
||||
serde_json::from_slice(&output.stdout).expect("secret list config JSON should parse");
|
||||
assert_eq!(value, Value::Array(vec![]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn completion_succeeds_with_json_output_format_from_home_config() {
|
||||
let context = test_context!();
|
||||
context.write_home(
|
||||
".fabro/settings.toml",
|
||||
"_version = 1\n\n[cli.output]\nformat = \"json\"\n",
|
||||
);
|
||||
|
||||
let output = context
|
||||
.command()
|
||||
.args(["completion", "zsh"])
|
||||
.output()
|
||||
.expect("command should run");
|
||||
|
||||
assert!(output.status.success());
|
||||
let stdout = output_stdout(&output);
|
||||
assert!(stdout.contains("#compdef"));
|
||||
assert!(serde_json::from_slice::<Value>(&output.stdout).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ps_supports_global_flag_and_env_var() {
|
||||
let context = test_context!();
|
||||
|
|
|
|||
|
|
@ -835,13 +835,13 @@ fn run_id_passthrough_uses_provided_ulid() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn json_run_implies_auto_approve_for_human_gates() {
|
||||
fn json_run_requires_manual_input_for_human_gates_without_auto_approve() {
|
||||
let context = test_context!();
|
||||
let workflow = context.temp_dir.join("human-gate.fabro");
|
||||
context.write_temp(
|
||||
"human-gate.fabro",
|
||||
r#"digraph HumanGate {
|
||||
graph [goal="Route through the default approval path"]
|
||||
graph [goal="Require explicit approval before continuing"]
|
||||
start [shape=Mdiamond, label="Start"]
|
||||
exit [shape=Msquare, label="Exit"]
|
||||
approve [shape=hexagon, label="Approve?"]
|
||||
|
|
@ -870,806 +870,31 @@ fn json_run_implies_auto_approve_for_human_gates() {
|
|||
.expect("command should execute");
|
||||
|
||||
assert!(
|
||||
output.status.success(),
|
||||
"command failed:\nstdout:\n{}\nstderr:\n{}",
|
||||
!output.status.success(),
|
||||
"command unexpectedly succeeded:\nstdout:\n{}\nstderr:\n{}",
|
||||
String::from_utf8_lossy(&output.stdout),
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
);
|
||||
|
||||
let mut progress: Vec<Value> = String::from_utf8(output.stdout)
|
||||
let stderr = String::from_utf8(output.stderr).expect("stderr should be UTF-8");
|
||||
assert!(
|
||||
stderr.contains("This run is waiting for human input, but --json is non-interactive."),
|
||||
"stderr should explain the non-interactive interview failure:\n{stderr}"
|
||||
);
|
||||
|
||||
let progress: Vec<Value> = String::from_utf8(output.stdout)
|
||||
.expect("stdout should be UTF-8")
|
||||
.lines()
|
||||
.filter(|line| !line.trim().is_empty())
|
||||
.map(|line| serde_json::from_str(line).expect("run JSON output should be JSONL"))
|
||||
.collect();
|
||||
for event in &mut progress {
|
||||
if let Some(properties) = event.get_mut("properties").and_then(Value::as_object_mut) {
|
||||
if properties.contains_key("manifest_blob") {
|
||||
properties.insert(
|
||||
"manifest_blob".to_string(),
|
||||
Value::String("[BLOB_ID]".to_string()),
|
||||
);
|
||||
}
|
||||
if properties.contains_key("definition_blob") {
|
||||
properties.insert(
|
||||
"definition_blob".to_string(),
|
||||
Value::String("[BLOB_ID]".to_string()),
|
||||
);
|
||||
}
|
||||
}
|
||||
// Strip fields that vary between runs (version, server stanzas that
|
||||
// carry machine-specific values, cli.target sockets).
|
||||
if let Some(settings) = event
|
||||
.pointer_mut("/properties/settings")
|
||||
.and_then(Value::as_object_mut)
|
||||
{
|
||||
settings.remove("_version");
|
||||
settings.remove("server");
|
||||
settings.remove("version");
|
||||
}
|
||||
if let Some(target) = event
|
||||
.pointer_mut("/properties/settings/cli/target")
|
||||
.and_then(Value::as_object_mut)
|
||||
{
|
||||
if target.contains_key("path") {
|
||||
target.insert(
|
||||
"path".to_string(),
|
||||
Value::String("[CLI_SOCKET]".to_string()),
|
||||
);
|
||||
}
|
||||
}
|
||||
let Some(model) = event.pointer_mut("/properties/settings/run/model") else {
|
||||
continue;
|
||||
};
|
||||
let Some(model) = model.as_object_mut() else {
|
||||
continue;
|
||||
};
|
||||
model.insert("name".to_string(), Value::String("[LLM_MODEL]".to_string()));
|
||||
model.insert(
|
||||
"provider".to_string(),
|
||||
Value::String("[LLM_PROVIDER]".to_string()),
|
||||
);
|
||||
}
|
||||
fabro_json_snapshot!(context, &progress, @r#"
|
||||
[
|
||||
{
|
||||
"actor": {
|
||||
"display": "dev",
|
||||
"id": "dev",
|
||||
"kind": "user"
|
||||
},
|
||||
"event": "run.created",
|
||||
"id": "[EVENT_ID]",
|
||||
"properties": {
|
||||
"graph": {
|
||||
"attrs": {
|
||||
"goal": {
|
||||
"String": "Route through the default approval path"
|
||||
}
|
||||
},
|
||||
"edges": [
|
||||
{
|
||||
"attrs": {},
|
||||
"from": "start",
|
||||
"to": "approve"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"label": {
|
||||
"String": "[A] Approve"
|
||||
}
|
||||
},
|
||||
"from": "approve",
|
||||
"to": "ship"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"label": {
|
||||
"String": "[R] Revise"
|
||||
}
|
||||
},
|
||||
"from": "approve",
|
||||
"to": "revise"
|
||||
},
|
||||
{
|
||||
"attrs": {},
|
||||
"from": "ship",
|
||||
"to": "exit"
|
||||
},
|
||||
{
|
||||
"attrs": {},
|
||||
"from": "revise",
|
||||
"to": "exit"
|
||||
}
|
||||
],
|
||||
"name": "HumanGate",
|
||||
"nodes": {
|
||||
"approve": {
|
||||
"attrs": {
|
||||
"label": {
|
||||
"String": "Approve?"
|
||||
},
|
||||
"shape": {
|
||||
"String": "hexagon"
|
||||
}
|
||||
},
|
||||
"id": "approve"
|
||||
},
|
||||
"exit": {
|
||||
"attrs": {
|
||||
"label": {
|
||||
"String": "Exit"
|
||||
},
|
||||
"shape": {
|
||||
"String": "Msquare"
|
||||
}
|
||||
},
|
||||
"id": "exit"
|
||||
},
|
||||
"revise": {
|
||||
"attrs": {
|
||||
"script": {
|
||||
"String": "echo revised"
|
||||
},
|
||||
"shape": {
|
||||
"String": "parallelogram"
|
||||
}
|
||||
},
|
||||
"id": "revise"
|
||||
},
|
||||
"ship": {
|
||||
"attrs": {
|
||||
"script": {
|
||||
"String": "echo shipped"
|
||||
},
|
||||
"shape": {
|
||||
"String": "parallelogram"
|
||||
}
|
||||
},
|
||||
"id": "ship"
|
||||
},
|
||||
"start": {
|
||||
"attrs": {
|
||||
"label": {
|
||||
"String": "Start"
|
||||
},
|
||||
"shape": {
|
||||
"String": "Mdiamond"
|
||||
}
|
||||
},
|
||||
"id": "start"
|
||||
}
|
||||
}
|
||||
},
|
||||
"host_repo_path": "[TEMP_DIR]",
|
||||
"manifest_blob": "[BLOB_ID]",
|
||||
"provenance": {
|
||||
"client": {
|
||||
"name": "fabro-cli",
|
||||
"user_agent": "fabro-cli/0.176.2",
|
||||
"version": "0.176.2"
|
||||
},
|
||||
"server": {
|
||||
"version": "0.176.2"
|
||||
},
|
||||
"subject": {
|
||||
"auth_method": "dev_token",
|
||||
"login": "dev"
|
||||
}
|
||||
},
|
||||
"run_dir": "[RUN_DIR]",
|
||||
"settings": {
|
||||
"workflow": {
|
||||
"graph": "workflow.fabro"
|
||||
},
|
||||
"cli": {
|
||||
"exec": {
|
||||
"prevent_idle_sleep": false
|
||||
},
|
||||
"output": {
|
||||
"format": "text",
|
||||
"verbosity": "normal"
|
||||
},
|
||||
"target": {
|
||||
"path": "[CLI_SOCKET]",
|
||||
"type": "unix"
|
||||
},
|
||||
"updates": {
|
||||
"check": true
|
||||
}
|
||||
},
|
||||
"features": {
|
||||
"session_sandboxes": false
|
||||
},
|
||||
"project": {
|
||||
"directory": "."
|
||||
},
|
||||
"run": {
|
||||
"execution": {
|
||||
"approval": "auto",
|
||||
"mode": "normal",
|
||||
"retros": false
|
||||
},
|
||||
"goal": "Route through the default approval path",
|
||||
"model": {
|
||||
"name": "[LLM_MODEL]",
|
||||
"provider": "[LLM_PROVIDER]"
|
||||
},
|
||||
"prepare": {
|
||||
"timeout": "5m"
|
||||
},
|
||||
"sandbox": {
|
||||
"devcontainer": false,
|
||||
"local": {
|
||||
"worktree_mode": "clean"
|
||||
},
|
||||
"preserve": false,
|
||||
"provider": "local"
|
||||
}
|
||||
}
|
||||
},
|
||||
"workflow_slug": "human-gate",
|
||||
"workflow_source": "digraph HumanGate {/n graph [goal=\"Route through the default approval path\"]/n start [shape=Mdiamond, label=\"Start\"]/n exit [shape=Msquare, label=\"Exit\"]/n approve [shape=hexagon, label=\"Approve?\"]/n ship [shape=parallelogram, script=\"echo shipped\"]/n revise [shape=parallelogram, script=\"echo revised\"]/n start -> approve/n approve -> ship [label=\"[A] Approve\"]/n approve -> revise [label=\"[R] Revise\"]/n ship -> exit/n revise -> exit/n}/n",
|
||||
"working_directory": "[TEMP_DIR]"
|
||||
},
|
||||
"run_id": "[ULID]",
|
||||
"ts": "[TIMESTAMP]"
|
||||
},
|
||||
{
|
||||
"event": "run.submitted",
|
||||
"id": "[EVENT_ID]",
|
||||
"properties": {
|
||||
"definition_blob": "[BLOB_ID]"
|
||||
},
|
||||
"run_id": "[ULID]",
|
||||
"ts": "[TIMESTAMP]"
|
||||
},
|
||||
{
|
||||
"event": "run.starting",
|
||||
"id": "[EVENT_ID]",
|
||||
"properties": {
|
||||
"reason": "sandbox_initializing"
|
||||
},
|
||||
"run_id": "[ULID]",
|
||||
"ts": "[TIMESTAMP]"
|
||||
},
|
||||
{
|
||||
"event": "sandbox.initializing",
|
||||
"id": "[EVENT_ID]",
|
||||
"properties": {
|
||||
"provider": "local"
|
||||
},
|
||||
"run_id": "[ULID]",
|
||||
"ts": "[TIMESTAMP]"
|
||||
},
|
||||
{
|
||||
"event": "sandbox.ready",
|
||||
"id": "[EVENT_ID]",
|
||||
"properties": {
|
||||
"duration_ms": "[DURATION_MS]",
|
||||
"provider": "local"
|
||||
},
|
||||
"run_id": "[ULID]",
|
||||
"ts": "[TIMESTAMP]"
|
||||
},
|
||||
{
|
||||
"event": "sandbox.initialized",
|
||||
"id": "[EVENT_ID]",
|
||||
"properties": {
|
||||
"provider": "local",
|
||||
"working_directory": "[TEMP_DIR]"
|
||||
},
|
||||
"run_id": "[ULID]",
|
||||
"ts": "[TIMESTAMP]"
|
||||
},
|
||||
{
|
||||
"event": "run.started",
|
||||
"id": "[EVENT_ID]",
|
||||
"properties": {
|
||||
"goal": "Route through the default approval path",
|
||||
"name": "HumanGate"
|
||||
},
|
||||
"run_id": "[ULID]",
|
||||
"ts": "[TIMESTAMP]"
|
||||
},
|
||||
{
|
||||
"event": "run.running",
|
||||
"id": "[EVENT_ID]",
|
||||
"properties": {},
|
||||
"run_id": "[ULID]",
|
||||
"ts": "[TIMESTAMP]"
|
||||
},
|
||||
{
|
||||
"event": "stage.started",
|
||||
"id": "[EVENT_ID]",
|
||||
"node_id": "start",
|
||||
"node_label": "Start",
|
||||
"properties": {
|
||||
"attempt": 1,
|
||||
"handler_type": "start",
|
||||
"index": 0,
|
||||
"max_attempts": 1
|
||||
},
|
||||
"run_id": "[ULID]",
|
||||
"stage_id": "start@1",
|
||||
"ts": "[TIMESTAMP]"
|
||||
},
|
||||
{
|
||||
"event": "stage.completed",
|
||||
"id": "[EVENT_ID]",
|
||||
"node_id": "start",
|
||||
"node_label": "Start",
|
||||
"properties": {
|
||||
"attempt": 1,
|
||||
"context_values": {
|
||||
"current.preamble": "Goal: Route through the default approval path/n",
|
||||
"current_node": "start",
|
||||
"graph.goal": "Route through the default approval path",
|
||||
"internal.fidelity": "compact",
|
||||
"internal.node_visit_count": 1,
|
||||
"internal.run_id": "[ULID]",
|
||||
"internal.thread_id": null
|
||||
},
|
||||
"duration_ms": "[DURATION_MS]",
|
||||
"index": 0,
|
||||
"max_attempts": 1,
|
||||
"node_visits": {
|
||||
"start": 1
|
||||
},
|
||||
"status": "success"
|
||||
},
|
||||
"run_id": "[ULID]",
|
||||
"stage_id": "start@1",
|
||||
"ts": "[TIMESTAMP]"
|
||||
},
|
||||
{
|
||||
"event": "edge.selected",
|
||||
"id": "[EVENT_ID]",
|
||||
"properties": {
|
||||
"from_node": "start",
|
||||
"is_jump": false,
|
||||
"reason": "unconditional",
|
||||
"stage_status": "success",
|
||||
"to_node": "approve"
|
||||
},
|
||||
"run_id": "[ULID]",
|
||||
"ts": "[TIMESTAMP]"
|
||||
},
|
||||
{
|
||||
"event": "checkpoint.completed",
|
||||
"id": "[EVENT_ID]",
|
||||
"node_id": "start",
|
||||
"node_label": "start",
|
||||
"properties": {
|
||||
"completed_nodes": [
|
||||
"start"
|
||||
],
|
||||
"context_values": {
|
||||
"current_node": "start",
|
||||
"failure_class": "",
|
||||
"failure_signature": "",
|
||||
"graph.goal": "Route through the default approval path",
|
||||
"internal.fidelity": "compact",
|
||||
"internal.node_visit_count": 1,
|
||||
"internal.retry_count.start": 0,
|
||||
"internal.run_id": "[ULID]",
|
||||
"internal.thread_id": null,
|
||||
"outcome": "success"
|
||||
},
|
||||
"current_node": "start",
|
||||
"next_node_id": "approve",
|
||||
"node_outcomes": {
|
||||
"start": {
|
||||
"status": "success",
|
||||
"usage": null
|
||||
}
|
||||
},
|
||||
"node_visits": {
|
||||
"start": 1
|
||||
},
|
||||
"status": "success"
|
||||
},
|
||||
"run_id": "[ULID]",
|
||||
"stage_id": "start@1",
|
||||
"ts": "[TIMESTAMP]"
|
||||
},
|
||||
{
|
||||
"event": "stage.started",
|
||||
"id": "[EVENT_ID]",
|
||||
"node_id": "approve",
|
||||
"node_label": "Approve?",
|
||||
"properties": {
|
||||
"attempt": 1,
|
||||
"handler_type": "human",
|
||||
"index": 1,
|
||||
"max_attempts": 1
|
||||
},
|
||||
"run_id": "[ULID]",
|
||||
"stage_id": "approve@1",
|
||||
"ts": "[TIMESTAMP]"
|
||||
},
|
||||
{
|
||||
"event": "interview.started",
|
||||
"id": "[EVENT_ID]",
|
||||
"node_id": "approve",
|
||||
"node_label": "approve",
|
||||
"properties": {
|
||||
"allow_freeform": false,
|
||||
"options": [
|
||||
{
|
||||
"key": "A",
|
||||
"label": "[A] Approve"
|
||||
},
|
||||
{
|
||||
"key": "R",
|
||||
"label": "[R] Revise"
|
||||
}
|
||||
],
|
||||
"question": "Approve?",
|
||||
"question_id": "[ULID]",
|
||||
"question_type": "multiple_choice",
|
||||
"stage": "approve"
|
||||
},
|
||||
"run_id": "[ULID]",
|
||||
"stage_id": "approve@1",
|
||||
"ts": "[TIMESTAMP]"
|
||||
},
|
||||
{
|
||||
"event": "interview.completed",
|
||||
"id": "[EVENT_ID]",
|
||||
"node_id": "approve",
|
||||
"node_label": "approve",
|
||||
"properties": {
|
||||
"answer": "A",
|
||||
"duration_ms": "[DURATION_MS]",
|
||||
"question": "Approve?",
|
||||
"question_id": "[ULID]"
|
||||
},
|
||||
"run_id": "[ULID]",
|
||||
"stage_id": "approve@1",
|
||||
"ts": "[TIMESTAMP]"
|
||||
},
|
||||
{
|
||||
"event": "stage.completed",
|
||||
"id": "[EVENT_ID]",
|
||||
"node_id": "approve",
|
||||
"node_label": "Approve?",
|
||||
"properties": {
|
||||
"attempt": 1,
|
||||
"context_updates": {
|
||||
"human.gate.label": "[A] Approve",
|
||||
"human.gate.selected": "A"
|
||||
},
|
||||
"context_values": {
|
||||
"current.preamble": "Goal: Route through the default approval path/n",
|
||||
"current_node": "approve",
|
||||
"failure_class": "",
|
||||
"failure_signature": "",
|
||||
"graph.goal": "Route through the default approval path",
|
||||
"internal.fidelity": "compact",
|
||||
"internal.node_visit_count": 1,
|
||||
"internal.retry_count.start": 0,
|
||||
"internal.run_id": "[ULID]",
|
||||
"internal.thread_id": "start",
|
||||
"outcome": "success",
|
||||
"thread.start.current_node": "approve"
|
||||
},
|
||||
"duration_ms": "[DURATION_MS]",
|
||||
"index": 1,
|
||||
"max_attempts": 1,
|
||||
"node_visits": {
|
||||
"approve": 1,
|
||||
"start": 1
|
||||
},
|
||||
"preferred_label": "[A] Approve",
|
||||
"status": "success",
|
||||
"suggested_next_ids": [
|
||||
"ship"
|
||||
]
|
||||
},
|
||||
"run_id": "[ULID]",
|
||||
"stage_id": "approve@1",
|
||||
"ts": "[TIMESTAMP]"
|
||||
},
|
||||
{
|
||||
"event": "edge.selected",
|
||||
"id": "[EVENT_ID]",
|
||||
"properties": {
|
||||
"from_node": "approve",
|
||||
"is_jump": false,
|
||||
"label": "[A] Approve",
|
||||
"preferred_label": "[A] Approve",
|
||||
"reason": "preferred_label",
|
||||
"stage_status": "success",
|
||||
"suggested_next_ids": [
|
||||
"ship"
|
||||
],
|
||||
"to_node": "ship"
|
||||
},
|
||||
"run_id": "[ULID]",
|
||||
"ts": "[TIMESTAMP]"
|
||||
},
|
||||
{
|
||||
"event": "checkpoint.completed",
|
||||
"id": "[EVENT_ID]",
|
||||
"node_id": "approve",
|
||||
"node_label": "approve",
|
||||
"properties": {
|
||||
"completed_nodes": [
|
||||
"start",
|
||||
"approve"
|
||||
],
|
||||
"context_values": {
|
||||
"current_node": "approve",
|
||||
"failure_class": "",
|
||||
"failure_signature": "",
|
||||
"graph.goal": "Route through the default approval path",
|
||||
"human.gate.label": "[A] Approve",
|
||||
"human.gate.selected": "A",
|
||||
"internal.fidelity": "compact",
|
||||
"internal.node_visit_count": 1,
|
||||
"internal.retry_count.approve": 0,
|
||||
"internal.retry_count.start": 0,
|
||||
"internal.run_id": "[ULID]",
|
||||
"internal.thread_id": "start",
|
||||
"outcome": "success",
|
||||
"preferred_label": "[A] Approve",
|
||||
"thread.start.current_node": "approve"
|
||||
},
|
||||
"current_node": "approve",
|
||||
"next_node_id": "ship",
|
||||
"node_outcomes": {
|
||||
"approve": {
|
||||
"context_updates": {
|
||||
"human.gate.label": "[A] Approve",
|
||||
"human.gate.selected": "A"
|
||||
},
|
||||
"preferred_label": "[A] Approve",
|
||||
"status": "success",
|
||||
"suggested_next_ids": [
|
||||
"ship"
|
||||
],
|
||||
"usage": null
|
||||
},
|
||||
"start": {
|
||||
"status": "success",
|
||||
"usage": null
|
||||
}
|
||||
},
|
||||
"node_visits": {
|
||||
"approve": 1,
|
||||
"start": 1
|
||||
},
|
||||
"status": "success"
|
||||
},
|
||||
"run_id": "[ULID]",
|
||||
"stage_id": "approve@1",
|
||||
"ts": "[TIMESTAMP]"
|
||||
},
|
||||
{
|
||||
"event": "stage.started",
|
||||
"id": "[EVENT_ID]",
|
||||
"node_id": "ship",
|
||||
"node_label": "ship",
|
||||
"properties": {
|
||||
"attempt": 1,
|
||||
"handler_type": "command",
|
||||
"index": 2,
|
||||
"max_attempts": 1
|
||||
},
|
||||
"run_id": "[ULID]",
|
||||
"stage_id": "ship@1",
|
||||
"ts": "[TIMESTAMP]"
|
||||
},
|
||||
{
|
||||
"event": "command.started",
|
||||
"id": "[EVENT_ID]",
|
||||
"node_id": "ship",
|
||||
"node_label": "ship",
|
||||
"properties": {
|
||||
"command": "echo shipped",
|
||||
"language": "shell",
|
||||
"script": "echo shipped"
|
||||
},
|
||||
"run_id": "[ULID]",
|
||||
"stage_id": "ship@1",
|
||||
"ts": "[TIMESTAMP]"
|
||||
},
|
||||
{
|
||||
"event": "command.completed",
|
||||
"id": "[EVENT_ID]",
|
||||
"node_id": "ship",
|
||||
"node_label": "ship",
|
||||
"properties": {
|
||||
"duration_ms": "[DURATION_MS]",
|
||||
"exit_code": 0,
|
||||
"stderr": "",
|
||||
"stdout": "shipped/n",
|
||||
"timed_out": false
|
||||
},
|
||||
"run_id": "[ULID]",
|
||||
"stage_id": "ship@1",
|
||||
"ts": "[TIMESTAMP]"
|
||||
},
|
||||
{
|
||||
"event": "stage.completed",
|
||||
"id": "[EVENT_ID]",
|
||||
"node_id": "ship",
|
||||
"node_label": "ship",
|
||||
"properties": {
|
||||
"attempt": 1,
|
||||
"context_updates": {
|
||||
"command.output": "shipped/n",
|
||||
"command.stderr": ""
|
||||
},
|
||||
"context_values": {
|
||||
"current.preamble": "Goal: Route through the default approval path/n/n## Completed stages/n- **approve**: success/n/n## Context/n- human.gate.label: [A] Approve/n- human.gate.selected: A/n",
|
||||
"current_node": "ship",
|
||||
"failure_class": "",
|
||||
"failure_signature": "",
|
||||
"graph.goal": "Route through the default approval path",
|
||||
"human.gate.label": "[A] Approve",
|
||||
"human.gate.selected": "A",
|
||||
"internal.fidelity": "compact",
|
||||
"internal.node_visit_count": 1,
|
||||
"internal.retry_count.approve": 0,
|
||||
"internal.retry_count.start": 0,
|
||||
"internal.run_id": "[ULID]",
|
||||
"internal.thread_id": "approve",
|
||||
"outcome": "success",
|
||||
"preferred_label": "[A] Approve",
|
||||
"thread.approve.current_node": "ship",
|
||||
"thread.start.current_node": "approve"
|
||||
},
|
||||
"duration_ms": "[DURATION_MS]",
|
||||
"index": 2,
|
||||
"max_attempts": 1,
|
||||
"node_visits": {
|
||||
"approve": 1,
|
||||
"ship": 1,
|
||||
"start": 1
|
||||
},
|
||||
"notes": "Script completed: echo shipped",
|
||||
"status": "success"
|
||||
},
|
||||
"run_id": "[ULID]",
|
||||
"stage_id": "ship@1",
|
||||
"ts": "[TIMESTAMP]"
|
||||
},
|
||||
{
|
||||
"event": "edge.selected",
|
||||
"id": "[EVENT_ID]",
|
||||
"properties": {
|
||||
"from_node": "ship",
|
||||
"is_jump": false,
|
||||
"reason": "unconditional",
|
||||
"stage_status": "success",
|
||||
"to_node": "exit"
|
||||
},
|
||||
"run_id": "[ULID]",
|
||||
"ts": "[TIMESTAMP]"
|
||||
},
|
||||
{
|
||||
"event": "checkpoint.completed",
|
||||
"id": "[EVENT_ID]",
|
||||
"node_id": "ship",
|
||||
"node_label": "ship",
|
||||
"properties": {
|
||||
"completed_nodes": [
|
||||
"start",
|
||||
"approve",
|
||||
"ship"
|
||||
],
|
||||
"context_values": {
|
||||
"command.output": "shipped/n",
|
||||
"command.stderr": "",
|
||||
"current_node": "ship",
|
||||
"failure_class": "",
|
||||
"failure_signature": "",
|
||||
"graph.goal": "Route through the default approval path",
|
||||
"human.gate.label": "[A] Approve",
|
||||
"human.gate.selected": "A",
|
||||
"internal.fidelity": "compact",
|
||||
"internal.node_visit_count": 1,
|
||||
"internal.retry_count.approve": 0,
|
||||
"internal.retry_count.ship": 0,
|
||||
"internal.retry_count.start": 0,
|
||||
"internal.run_id": "[ULID]",
|
||||
"internal.thread_id": "approve",
|
||||
"outcome": "success",
|
||||
"preferred_label": "[A] Approve",
|
||||
"thread.approve.current_node": "ship",
|
||||
"thread.start.current_node": "approve"
|
||||
},
|
||||
"current_node": "ship",
|
||||
"next_node_id": "exit",
|
||||
"node_outcomes": {
|
||||
"approve": {
|
||||
"context_updates": {
|
||||
"human.gate.label": "[A] Approve",
|
||||
"human.gate.selected": "A"
|
||||
},
|
||||
"preferred_label": "[A] Approve",
|
||||
"status": "success",
|
||||
"suggested_next_ids": [
|
||||
"ship"
|
||||
],
|
||||
"usage": null
|
||||
},
|
||||
"ship": {
|
||||
"context_updates": {
|
||||
"command.output": "shipped/n",
|
||||
"command.stderr": ""
|
||||
},
|
||||
"notes": "Script completed: echo shipped",
|
||||
"status": "success",
|
||||
"usage": null
|
||||
},
|
||||
"start": {
|
||||
"status": "success",
|
||||
"usage": null
|
||||
}
|
||||
},
|
||||
"node_visits": {
|
||||
"approve": 1,
|
||||
"ship": 1,
|
||||
"start": 1
|
||||
},
|
||||
"status": "success"
|
||||
},
|
||||
"run_id": "[ULID]",
|
||||
"stage_id": "ship@1",
|
||||
"ts": "[TIMESTAMP]"
|
||||
},
|
||||
{
|
||||
"event": "stage.started",
|
||||
"id": "[EVENT_ID]",
|
||||
"node_id": "exit",
|
||||
"node_label": "Exit",
|
||||
"properties": {
|
||||
"attempt": 1,
|
||||
"handler_type": "exit",
|
||||
"index": 3,
|
||||
"max_attempts": 1
|
||||
},
|
||||
"run_id": "[ULID]",
|
||||
"stage_id": "exit@1",
|
||||
"ts": "[TIMESTAMP]"
|
||||
},
|
||||
{
|
||||
"event": "stage.completed",
|
||||
"id": "[EVENT_ID]",
|
||||
"node_id": "exit",
|
||||
"node_label": "Exit",
|
||||
"properties": {
|
||||
"attempt": 1,
|
||||
"duration_ms": "[DURATION_MS]",
|
||||
"index": 3,
|
||||
"max_attempts": 1,
|
||||
"status": "success"
|
||||
},
|
||||
"run_id": "[ULID]",
|
||||
"stage_id": "exit@1",
|
||||
"ts": "[TIMESTAMP]"
|
||||
},
|
||||
{
|
||||
"event": "run.completed",
|
||||
"id": "[EVENT_ID]",
|
||||
"properties": {
|
||||
"artifact_count": 0,
|
||||
"duration_ms": "[DURATION_MS]",
|
||||
"reason": "completed",
|
||||
"status": "success"
|
||||
},
|
||||
"run_id": "[ULID]",
|
||||
"ts": "[TIMESTAMP]"
|
||||
}
|
||||
]
|
||||
"#);
|
||||
|
||||
assert_eq!(
|
||||
progress[0].pointer("/properties/settings/run/execution/approval"),
|
||||
Some(&serde_json::json!("auto"))
|
||||
assert!(
|
||||
progress
|
||||
.iter()
|
||||
.any(|event| event.get("event") == Some(&Value::String("interview.started".into()))),
|
||||
"stdout should include the interview start event:\n{}",
|
||||
serde_json::to_string_pretty(&progress).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue