1pub mod application;
2pub mod cli;
3
4use clap::{CommandFactory, FromArgMatches};
5use std::io::IsTerminal;
6
7pub async fn run() {
8 let filter = tracing_subscriber::EnvFilter::try_from_default_env()
9 .unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info"));
10 tracing_subscriber::fmt().with_env_filter(filter).init();
11
12 let mut cmd = cli::args::Cli::command();
13 cmd = cmd.styles(cli::style::kagi_styles());
14
15 let tty = std::io::stdout().is_terminal();
16 let c = cli::style::Palette::new(tty);
17 let logo = c.accent(
18 r#" _ _
19 | | ____ _ __ _(_)
20 | |/ / _` |/ _` | |
21 | < (_| | (_| | |
22 |_|\_\__,_|\__, |_|
23 |___/ K"#,
24 );
25 #[cfg(not(feature = "server"))]
26 let cmd_lines: Vec<(&str, &str)> = vec![
27 ("init", "create a team-ready encrypted project"),
28 ("set", "store one encrypted value"),
29 ("run", "start a process with injected env vars"),
30 (
31 "get",
32 "show service/env keys or print one value after confirmation",
33 ),
34 ("export", "print KEY=value lines after confirmation"),
35 ("import", "import values from a .env file"),
36 ("sync", "sync keys from .env.example"),
37 ("file", "manage encrypted files"),
38 ("env", "manage default environments"),
39 ("member", "request, list, approve, or remove members"),
40 ];
41 #[cfg(feature = "server")]
42 let cmd_lines: Vec<(&str, &str)> = vec![
43 ("init", "create a team-ready encrypted project"),
44 ("set", "store one encrypted value"),
45 ("run", "start a process with injected env vars"),
46 (
47 "get",
48 "show service/env keys or print one value after confirmation",
49 ),
50 ("export", "print KEY=value lines after confirmation"),
51 ("import", "import values from a .env file"),
52 ("sync", "sync keys from .env.example"),
53 ("file", "manage encrypted files"),
54 ("env", "manage default environments"),
55 ("member", "request, list, approve, or remove members"),
56 ("serve", "start the remote sync server"),
57 ("remote", "login, register, sync, and administer remotes"),
58 ];
59 let max_cmd = cmd_lines.iter().map(|(n, _)| n.len()).max().unwrap_or(0);
60 let cmd_list = cmd_lines
61 .iter()
62 .map(|(name, desc)| {
63 let pad = " ".repeat(max_cmd.saturating_sub(name.len()));
64 format!(" {}{} {}", c.accent(name), pad, c.muted(desc))
65 })
66 .collect::<Vec<_>>()
67 .join("\n");
68
69 let flow_lines: Vec<(&str, &str)> = vec![
70 ("init", "--envs development,production"),
71 ("set", "api DATABASE_URL '<value>'"),
72 ("run", "api bun dev"),
73 ];
74 let max_flow = flow_lines.iter().map(|(n, _)| n.len()).max().unwrap_or(0);
75 let flow_list = flow_lines
76 .iter()
77 .map(|(name, args)| {
78 let pad = " ".repeat(max_flow.saturating_sub(name.len()));
79 format!(
80 " {} {}{} {}",
81 c.accent("kagi"),
82 c.accent(name),
83 pad,
84 c.muted(args)
85 )
86 })
87 .collect::<Vec<_>>()
88 .join("\n");
89
90 let cmd_ref = format!(
91 "{logo}\n{rule}\n{tagline}\n\n{usage}\n {kagi} {command}\n {kagi} {command_help}\n\n{flow}\n{flow_list}\n\n{commands}\n{cmd_list}\n\n{security}\n {security_note}",
92 rule = c.warning("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"),
93 tagline = c.muted("Encrypted envs, scoped safely."),
94 usage = c.warning("Usage"),
95 kagi = c.accent("kagi"),
96 command = c.key("<command>"),
97 command_help = c.key("<command> --help"),
98 flow = c.warning("Core Flow"),
99 flow_list = flow_list,
100 commands = c.warning("Commands"),
101 cmd_list = cmd_list,
102 security = c.warning("Security"),
103 security_note =
104 c.muted("Use kagi run for scripts. get --show/export require a terminal prompt."),
105 );
106 cmd = cmd.before_help(cmd_ref);
107 cmd = cmd.help_template("{before-help}");
108
109 if std::env::args_os().len() == 1 {
110 if let Err(e) = cmd.print_help() {
111 eprintln!("{e}");
112 std::process::exit(1);
113 }
114 println!();
115 return;
116 }
117
118 let matches = cmd.get_matches();
119 let cli = match cli::args::Cli::from_arg_matches(&matches) {
120 Ok(c) => c,
121 Err(e) => e.exit(),
122 };
123 if let Err(e) = cli::commands::run(cli).await {
124 let tty = std::io::stdout().is_terminal();
125 let c = cli::style::Palette::new(tty);
126 eprintln!("{} {}", c.prefix(), c.error(&format!("error: {e}")));
127 std::process::exit(1);
128 }
129}