Skip to main content

max / makenotwork

4.3 KB · 148 lines History Blame Raw
1 //! PoM CLI entry point — parses subcommands and dispatches to handlers or MCP server.
2
3 use clap::{Parser, Subcommand};
4 use rmcp::ServiceExt;
5 use tokio::io::{stdin, stdout};
6 use tracing::info;
7 use tracing_subscriber::{fmt, prelude::*, EnvFilter};
8
9 use pom::config::{self, Config};
10 use pom::db;
11 use pom::error::Result;
12 use pom::tools::PomServer;
13
14 mod cli;
15
16 #[derive(Parser)]
17 #[command(name = "pom", about = "Peace of Mind — health checks and test orchestration")]
18 struct Cli {
19 /// Path to config file (default: ~/.config/pom/pom.toml)
20 #[arg(long, global = true)]
21 config: Option<std::path::PathBuf>,
22
23 #[command(subcommand)]
24 command: Option<Commands>,
25 }
26
27 #[derive(Subcommand)]
28 enum Commands {
29 /// Check health of targets
30 Health {
31 /// Target name (omit for all)
32 target: Option<String>,
33 /// Output as JSON
34 #[arg(long)]
35 json: bool,
36 },
37 /// Run tests on a target via SSH
38 Test {
39 /// Target name
40 target: String,
41 /// Filter tests
42 #[arg(long, short)]
43 filter: Option<String>,
44 /// Output as JSON
45 #[arg(long)]
46 json: bool,
47 },
48 /// Show status dashboard
49 Status {
50 /// Output as JSON
51 #[arg(long)]
52 json: bool,
53 },
54 /// View history
55 History {
56 #[command(subcommand)]
57 kind: cli::HistoryKind,
58 },
59 /// Prune old records
60 Prune {
61 /// Number of days to keep (default 30)
62 #[arg(long, default_value = "30")]
63 days: i64,
64 },
65 /// Run DNS and WHOIS checks
66 Dns {
67 /// Target name (omit for all)
68 target: Option<String>,
69 /// Output as JSON
70 #[arg(long)]
71 json: bool,
72 },
73 /// Run as a daemon, checking health at intervals
74 Serve,
75 /// Show peer mesh status
76 Mesh {
77 /// Output as JSON
78 #[arg(long)]
79 json: bool,
80 },
81 }
82
83 #[tokio::main]
84 async fn main() -> Result<()> {
85 // Install the default rustls crypto provider before any TLS operations.
86 // Both aws-lc-rs and ring are in the dependency tree (via reqwest and tokio-rustls),
87 // so rustls can't auto-detect which to use.
88 let _ = tokio_rustls::rustls::crypto::ring::default_provider().install_default();
89
90 let cli = Cli::parse();
91
92 let config_path = cli.config.as_deref();
93 let config = Config::load(config_path)?;
94
95 match cli.command {
96 None => run_mcp_server(config).await,
97 Some(cmd) => run_cli(cmd, config).await,
98 }
99 }
100
101 async fn run_mcp_server(config: Config) -> Result<()> {
102 tracing_subscriber::registry()
103 .with(fmt::layer().with_writer(std::io::stderr))
104 .with(EnvFilter::from_default_env().add_directive("pom=info".parse()?))
105 .init();
106
107 info!("Starting PoM MCP server");
108
109 let db_path = config::db_path()?;
110 let pool = db::connect(&db_path).await?;
111 info!("Database ready at {}", db_path.display());
112
113 let server = PomServer::new(pool, config);
114 let transport = (stdin(), stdout());
115
116 info!("MCP server ready");
117 let service = server.serve(transport).await?;
118 let quit_reason = service.waiting().await?;
119 info!(?quit_reason, "MCP server shutting down");
120
121 Ok(())
122 }
123
124 async fn run_cli(
125 cmd: Commands,
126 config: Config,
127 ) -> Result<()> {
128 let log_level = if matches!(cmd, Commands::Serve) { "pom=info" } else { "pom=warn" };
129 tracing_subscriber::registry()
130 .with(fmt::layer().with_writer(std::io::stderr))
131 .with(EnvFilter::from_default_env().add_directive(log_level.parse()?))
132 .init();
133
134 let db_path = config::db_path()?;
135 let pool = db::connect(&db_path).await?;
136
137 match cmd {
138 Commands::Health { target, json } => cli::cmd_health(&pool, &config, target.as_deref(), json).await,
139 Commands::Test { target, filter, json } => cli::cmd_test(&pool, &config, &target, filter.as_deref(), json).await,
140 Commands::Status { json } => cli::cmd_status(&pool, &config, json).await,
141 Commands::History { kind } => cli::cmd_history(&pool, kind).await,
142 Commands::Prune { days } => cli::cmd_prune(&pool, days).await,
143 Commands::Dns { target, json } => cli::cmd_dns(&pool, &config, target.as_deref(), json).await,
144 Commands::Serve => cli::cmd_serve(&pool, &config).await,
145 Commands::Mesh { json } => cli::cmd_mesh(&config, json).await,
146 }
147 }
148