use std::io::IsTerminal;
use crate::cli::output::OutputConfig;
use crate::cli::{CapabilityArgs, Interaction};
use crate::commands::capability;
use crate::core::config::{set_audited, Config};
use crate::core::error::SsError;
use crate::core::registry;
#[derive(Debug, PartialEq, Eq)]
pub(crate) enum AuditAction {
NoOp,
PersistOnly,
Prompt,
}
pub(crate) fn decide(
audited: Option<bool>,
interactive: bool,
registry_empty: bool,
) -> AuditAction {
if audited == Some(true) {
return AuditAction::NoOp;
}
if !interactive {
return AuditAction::NoOp;
}
if registry_empty {
return AuditAction::PersistOnly;
}
AuditAction::Prompt
}
pub async fn maybe_first_run_audit(inter: Interaction, output: &OutputConfig) {
let _ = try_first_run_audit(inter, output).await;
}
async fn try_first_run_audit(inter: Interaction, output: &OutputConfig) -> Result<(), SsError> {
let config = Config::load()?;
let interactive = !inter.non_interactive
&& !output.is_json()
&& !output.is_quiet()
&& std::io::stderr().is_terminal();
let registry_empty = registry::load().map(|r| r.is_empty()).unwrap_or(true);
match decide(config.audited, interactive, registry_empty) {
AuditAction::NoOp => Ok(()),
AuditAction::PersistOnly => {
let _ = set_audited(true);
Ok(())
}
AuditAction::Prompt => {
let choice = inquire::Select::new(
"First run: audit everything already installed across your agents?",
vec![
"Yes — public report",
"Yes — private (unlisted) report",
"Skip",
],
)
.prompt()
.unwrap_or("Skip");
let _ = set_audited(true);
let private = match choice {
"Yes — public report" => Some(false),
"Yes — private (unlisted) report" => Some(true),
_ => None,
};
if let Some(private) = private {
let _ = capability::run_capability(
&CapabilityArgs {
target: None,
to: vec![],
private,
detailed: false,
},
output,
)
.await;
}
Ok(())
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn already_audited_is_noop() {
assert_eq!(decide(Some(true), true, false), AuditAction::NoOp);
assert_eq!(decide(Some(true), true, true), AuditAction::NoOp);
}
#[test]
fn non_interactive_is_noop() {
assert_eq!(decide(None, false, false), AuditAction::NoOp);
assert_eq!(decide(Some(false), false, false), AuditAction::NoOp);
}
#[test]
fn empty_registry_persists_without_prompt() {
assert_eq!(decide(None, true, true), AuditAction::PersistOnly);
}
#[test]
fn first_interactive_run_with_installs_prompts() {
assert_eq!(decide(None, true, false), AuditAction::Prompt);
assert_eq!(decide(Some(false), true, false), AuditAction::Prompt);
}
}