#![deny(unsafe_code, missing_debug_implementations)]
#![warn(missing_docs)]
mod cmd;
mod config;
mod exit;
mod output;
mod paths;
use clap::{Parser, Subcommand};
use tracing_subscriber::EnvFilter;
use crate::cmd::{
audit::AuditSub, completions::CompletionsArgs, context::ContextSub, init::InitArgs,
session::SessionSub,
};
use crate::exit::Exit;
const COMMAND_OBSERVABILITY_OPERATION: &str = "cli.command";
const COMMAND_OBSERVABILITY_SCHEMA_VERSION: u64 = 1;
#[derive(Debug, Parser)]
#[command(
name = "cortex",
version,
about = "Longitudinal supervisory memory substrate — CLI entrypoint",
propagate_version = true
)]
pub(crate) struct Cli {
#[arg(long = "print-config")]
print_config: bool,
#[arg(long = "json")]
json: bool,
#[command(subcommand)]
cmd: Option<Cmd>,
}
#[derive(Debug, Subcommand)]
enum Cmd {
Init(InitArgs),
Ingest(cmd::ingest::IngestArgs),
Audit {
#[command(subcommand)]
sub: AuditSub,
},
Backup(cmd::backup::BackupArgs),
Release {
#[command(subcommand)]
sub: cmd::release::ReleaseSub,
},
Compliance {
#[command(subcommand)]
sub: cmd::compliance::ComplianceSub,
},
Decay {
#[command(subcommand)]
sub: cmd::decay::DecaySub,
},
Reflect(cmd::reflect::ReflectArgs),
Memory {
#[command(subcommand)]
sub: cmd::memory::MemorySub,
},
Principles {
#[command(subcommand)]
sub: cmd::principles::PrinciplesSub,
},
Principle {
#[command(subcommand)]
sub: cmd::principle_promote::PrincipleSub,
},
Context {
#[command(subcommand)]
sub: ContextSub,
},
Proof {
#[command(subcommand)]
sub: cmd::proof::ProofSub,
},
Migrate {
#[command(subcommand)]
sub: cmd::migrate::MigrateSub,
},
Restore {
#[command(subcommand)]
sub: cmd::restore::RestoreSub,
},
Doctor(cmd::doctor::DoctorArgs),
Sign(cmd::sign::SignArgs),
Run(cmd::run::RunArgs),
RunLedgerDrill(cmd::run_ledger_drill::RunLedgerDrillArgs),
Session {
#[command(subcommand)]
sub: SessionSub,
},
Completions(CompletionsArgs),
Serve(cmd::serve::ServeArgs),
Models {
#[command(subcommand)]
sub: cmd::models::ModelsSub,
},
}
fn main() {
tracing_subscriber::fmt()
.with_env_filter(EnvFilter::from_default_env())
.with_writer(std::io::stderr)
.init();
let cli = match Cli::try_parse() {
Ok(c) => c,
Err(e) => {
e.print().ok();
std::process::exit(if e.use_stderr() {
Exit::Usage as i32
} else {
Exit::Ok as i32
});
}
};
output::set_json_mode(cli.json);
let command = cli.command_name();
let correlation_id = new_command_correlation_id();
output::set_correlation_id(correlation_id.clone());
tracing::info!(
audit_schema_version = COMMAND_OBSERVABILITY_SCHEMA_VERSION,
operation = COMMAND_OBSERVABILITY_OPERATION,
correlation_id = %correlation_id,
command,
proof_state = "UNKNOWN",
authority_class = "diagnostic_only",
diagnostic_only = true,
"command_started"
);
let exit = dispatch(cli);
tracing::info!(
audit_schema_version = COMMAND_OBSERVABILITY_SCHEMA_VERSION,
operation = COMMAND_OBSERVABILITY_OPERATION,
correlation_id = %correlation_id,
command,
exit_code = exit.code(),
status = exit_status(exit),
proof_state = "UNKNOWN",
authority_class = "diagnostic_only",
diagnostic_only = true,
"command_completed"
);
std::process::exit(exit.code());
}
fn dispatch(cli: Cli) -> Exit {
if cli.print_config {
if cli.cmd.is_some() {
eprintln!("cortex --print-config: cannot be combined with a subcommand");
return Exit::Usage;
}
return config::print_effective_config();
}
match cli.cmd {
Some(Cmd::Init(args)) => cmd::init::run(args),
Some(Cmd::Ingest(args)) => cmd::ingest::run(args),
Some(Cmd::Audit { sub }) => cmd::audit::run(sub),
Some(Cmd::Backup(args)) => cmd::backup::run(args),
Some(Cmd::Release { sub }) => cmd::release::run(sub),
Some(Cmd::Compliance { sub }) => cmd::compliance::run(sub),
Some(Cmd::Decay { sub }) => cmd::decay::run(sub),
Some(Cmd::Reflect(args)) => cmd::reflect::run(args),
Some(Cmd::Memory { sub }) => cmd::memory::run(sub),
Some(Cmd::Principles { sub }) => cmd::principles::run(sub),
Some(Cmd::Principle { sub }) => cmd::principle_promote::run(sub),
Some(Cmd::Context { sub }) => cmd::context::run(sub),
Some(Cmd::Proof { sub }) => cmd::proof::run(sub),
Some(Cmd::Migrate { sub }) => cmd::migrate::run(sub),
Some(Cmd::Restore { sub }) => cmd::restore::run(sub),
Some(Cmd::Doctor(args)) => cmd::doctor::run(args),
Some(Cmd::Sign(args)) => cmd::sign::run(args),
Some(Cmd::Run(args)) => cmd::run::run(args),
Some(Cmd::RunLedgerDrill(args)) => cmd::run_ledger_drill::run(args),
Some(Cmd::Session { sub }) => cmd::session::run(sub),
Some(Cmd::Completions(args)) => cmd::completions::run(args),
Some(Cmd::Serve(args)) => cmd::serve::run(args),
Some(Cmd::Models { sub }) => cmd::models::run(sub),
None => {
if output::json_enabled() {
let payload = serde_json::json!({ "detail": "missing subcommand" });
let envelope = output::Envelope::new("cortex", Exit::Usage, payload);
return output::emit(&envelope, Exit::Usage);
}
eprintln!("cortex: missing subcommand; use --help for usage");
Exit::Usage
}
}
}
impl Cli {
fn command_name(&self) -> &'static str {
if self.print_config {
return "print_config";
}
match self.cmd.as_ref() {
Some(cmd) => cmd.command_name(),
None => "none",
}
}
}
impl Cmd {
fn command_name(&self) -> &'static str {
match self {
Cmd::Init(_) => "init",
Cmd::Ingest(_) => "ingest",
Cmd::Audit { .. } => "audit",
Cmd::Backup(_) => "backup",
Cmd::Release { .. } => "release",
Cmd::Compliance { .. } => "compliance",
Cmd::Decay { .. } => "decay",
Cmd::Reflect(_) => "reflect",
Cmd::Memory { .. } => "memory",
Cmd::Principles { .. } => "principles",
Cmd::Principle { .. } => "principle",
Cmd::Context { .. } => "context",
Cmd::Proof { .. } => "proof",
Cmd::Migrate { .. } => "migrate",
Cmd::Restore { .. } => "restore",
Cmd::Doctor(_) => "doctor",
Cmd::Sign(_) => "sign",
Cmd::Run(_) => "run",
Cmd::RunLedgerDrill(_) => "run-ledger-drill",
Cmd::Session { .. } => "session",
Cmd::Completions(_) => "completions",
Cmd::Serve(_) => "serve",
Cmd::Models { .. } => "models",
}
}
}
fn new_command_correlation_id() -> String {
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_nanos();
format!("cmd_{now:x}_{:x}", std::process::id())
}
fn exit_status(exit: Exit) -> &'static str {
if exit == Exit::Ok {
"ok"
} else {
"error"
}
}