1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
//! `cleanlib audit` (cycle-7 Cli2). Migrates `cmd_audit` from `main.rs`.
use anyhow::Result;
use cleanlib_client::{config, transport};
use comfy_table::{presets, ContentArrangement, Table};
use crate::render::terminal::{stdout_is_tty, style_decision};
pub async fn run(
since: Option<String>,
decision: Option<String>,
ecosystem: Option<String>,
output: String,
) -> Result<()> {
let path = config::default_path();
let cfg = config::load_with_env_overrides(path.as_deref())?;
let client = transport::Client::from_config(&cfg)?;
let resp = client
.audit(since.as_deref(), decision.as_deref(), ecosystem.as_deref())
.await?;
match output.as_str() {
"json" => println!("{}", serde_json::to_string_pretty(&resp)?),
_ => {
if resp.records.is_empty() {
println!("(no audit entries match the given filters)");
} else {
let mut table = Table::new();
let preset = if stdout_is_tty() {
presets::UTF8_BORDERS_ONLY
} else {
presets::NOTHING
};
table
.load_preset(preset)
.set_content_arrangement(ContentArrangement::Dynamic)
.set_header(vec![
"AT", "REQUEST_ID", "ECOSYSTEM", "PACKAGE", "VERSION", "DECISION", "REASON",
]);
// CLEANLIB-366: field names below track the App-side `AuditRow`
// shape (request_at / package_name / package_version /
// policy_decision / reasoning). Prior code referenced `at` /
// `package` / `version` / `decision` / `reason` and silently
// rendered empty cells because deserialize-default filled them
// with `""`.
for e in &resp.records {
table.add_row(vec![
e.request_at.clone(),
e.request_id.clone(),
e.ecosystem.clone(),
e.package_name.clone(),
e.package_version.clone(),
style_decision(&e.policy_decision),
e.reasoning.clone(),
]);
}
println!("{table}");
}
// Honesty signal from the App: surface `not_wired` / `read_error`
// so the customer can distinguish "no matching rows" from "the
// audit backend is offline" (CLEANLIB-101 wire-in). Silent on
// `wired` — that's the normal path.
match resp.backend_status.as_str() {
"" | "wired" => {}
other => eprintln!("# audit backend status: {}", other),
}
}
}
Ok(())
}