use std::path::PathBuf;
use anyhow::{Context, Result};
use clap::{Args, Subcommand};
use agentstatedeveloper_core::{AuditEvent, read_jsonl};
use crate::config::Config;
#[derive(Debug, Subcommand)]
pub enum AuditCmd {
Tail(TailArgs),
Verify(VerifyArgs),
}
#[derive(Debug, Args)]
pub struct VerifyArgs {
#[arg(long)]
pub log: Option<PathBuf>,
}
#[derive(Debug, Args)]
pub struct TailArgs {
#[arg(long)]
pub log: Option<PathBuf>,
#[arg(long)]
pub event_type: Option<String>,
#[arg(long)]
pub since: Option<String>,
#[arg(long)]
pub actor: Option<String>,
#[arg(long)]
pub outcome: Option<String>,
#[arg(long, default_value_t = 200)]
pub limit: usize,
}
pub fn run(cfg: &Config, cmd: AuditCmd) -> Result<()> {
match cmd {
AuditCmd::Tail(args) => tail(cfg, args),
AuditCmd::Verify(args) => verify(cfg, args),
}
}
fn verify(_cfg: &Config, _args: VerifyArgs) -> Result<()> {
anyhow::bail!(
"audit verify is a commercial feature (Enterprise tier) — \
install asd-pro to enable tamper-evident chain verification. \
See https://agentstatedeveloper.dev/pricing"
)
}
fn tail(cfg: &Config, args: TailArgs) -> Result<()> {
let path = args
.log
.as_ref()
.or(cfg.audit_log_path.as_ref())
.ok_or_else(|| {
anyhow::anyhow!(
"no audit log configured — pass --log <path>, \
set --audit-log <path> globally, or export ASD_AUDIT_LOG"
)
})?;
let events = read_jsonl(path).with_context(|| format!("read audit log {}", path.display()))?;
let start_idx = match args.since {
Some(ref id) => events
.iter()
.position(|e| &e.event_id == id)
.map(|i| i + 1)
.unwrap_or(0),
None => 0,
};
let filtered: Vec<&AuditEvent> = events[start_idx..]
.iter()
.filter(|e| {
if let Some(ref t) = args.event_type {
if !e.event_type.contains(t) {
return false;
}
}
if let Some(ref a) = args.actor {
if &e.actor_id != a {
return false;
}
}
if let Some(ref o) = args.outcome {
if &e.outcome != o {
return false;
}
}
true
})
.take(args.limit)
.collect();
let out = serde_json::json!({
"path": path.display().to_string(),
"count": filtered.len(),
"events": filtered,
});
println!("{}", serde_json::to_string_pretty(&out)?);
Ok(())
}