1#![doc = include_str!("../README.md")]
2#![allow(
3 clippy::print_stdout,
4 clippy::print_stderr,
5 reason = "The CLI uses stdout/stderr for user interaction"
6)]
7
8use bitwarden_cli::install_color_eyre;
9use bitwarden_core::{DeviceType, GlobalClient, HostPlatformInfo, init_host_platform_info};
10use clap::{CommandFactory, Parser};
11use color_eyre::eyre::Result;
12use tracing_subscriber::{
13 EnvFilter, prelude::__tracing_subscriber_SubscriberExt as _, util::SubscriberInitExt as _,
14};
15
16use crate::{
17 client_state::{BwCommandExt, ClientContext},
18 command::*,
19 render::CommandResult,
20};
21
22mod admin_console;
23mod auth;
24mod client_state;
25mod command;
26mod dirt;
27mod key_management;
28mod platform;
29mod render;
30mod tools;
31mod vault;
32
33#[tokio::main(flavor = "current_thread")]
34async fn main() -> Result<()> {
35 let filter = EnvFilter::builder()
40 .with_default_directive(
41 option_env!("RUST_LOG")
42 .unwrap_or("info")
43 .parse()
44 .expect("should provide valid log level at compile time."),
45 )
46 .from_env_lossy();
49
50 tracing_subscriber::registry()
51 .with(tracing_subscriber::fmt::layer().with_writer(std::io::stderr))
52 .with(filter)
53 .init();
54
55 init_cli_platform_info();
56
57 let cli = Cli::parse();
58 install_color_eyre(cli.color)?;
59 let render_config = render::RenderConfig::new(&cli);
60
61 let Some(command) = cli.command else {
62 let mut cmd = Cli::command();
63 cmd.print_help()?;
64 return Ok(());
65 };
66
67 let result = process_commands(command, cli.session).await;
68
69 render_config.render_result(result)
71}
72
73async fn process_commands(command: Commands, _session: Option<String>) -> CommandResult {
74 let global = GlobalClient::new();
75
76 let user = if let (Ok(email), Ok(password)) =
79 (std::env::var("BW_EMAIL"), std::env::var("BW_PASSWORD"))
80 {
81 let client = bitwarden_pm::PasswordManagerClient::new(None);
82 temp_login(&client.0, email, password).await?;
83 Some(client)
84 } else {
85 None
86 };
87
88 let ctx = ClientContext { global, user };
89
90 match command {
91 Commands::Login(args) => args.run().await,
93 Commands::Logout => todo!(),
94
95 Commands::Lock(args) => args.dispatch(ctx).await,
97 Commands::Unlock(_args) => todo!(),
98
99 Commands::Sync(args) => args.dispatch(ctx).await,
101 Commands::Encode(args) => args.dispatch(ctx).await,
102 Commands::Config { command } => command.dispatch(ctx).await,
103 Commands::Completion(args) => args.dispatch(ctx).await,
104
105 Commands::Update { .. } => todo!(),
106
107 Commands::Status(_) => todo!(),
108
109 Commands::List { .. } => todo!(),
111 Commands::Get { command } => command.run(),
112 Commands::Create { command } => command.run(),
113 Commands::Edit { .. } => todo!(),
114 Commands::Delete { .. } => todo!(),
115 Commands::Restore(_args) => todo!(),
116
117 Commands::Confirm { .. } => todo!(),
119 Commands::DeviceApproval => todo!(),
120 Commands::Move(_args) => todo!(),
121
122 Commands::Generate(args) => {
124 let client = ctx
125 .user
126 .unwrap_or_else(|| bitwarden_pm::PasswordManagerClient::new(None));
127 args.run(&client)
128 }
129 Commands::Import(_args) => todo!(),
130 Commands::Export(_args) => todo!(),
131 Commands::Send(_args) => todo!(),
132 Commands::Receive(_args) => todo!(),
133
134 Commands::Serve(_args) => todo!(),
136 }
137}
138
139async fn temp_login(
143 client: &bitwarden_core::Client,
144 email: String,
145 password: String,
146) -> color_eyre::eyre::Result<()> {
147 use bitwarden_core::auth::login::PasswordLoginRequest;
148
149 let result = client
150 .auth()
151 .login_password(&PasswordLoginRequest {
152 email,
153 password,
154 two_factor: None,
155 })
156 .await?;
157
158 tracing::info!("Login result: {:?}", result);
159
160 Ok(())
161}
162
163fn init_cli_platform_info() {
164 let device_type = if cfg!(target_os = "windows") {
165 DeviceType::WindowsCLI
166 } else if cfg!(target_os = "macos") {
167 DeviceType::MacOsCLI
168 } else {
169 DeviceType::LinuxCLI
170 };
171
172 init_host_platform_info(HostPlatformInfo {
173 user_agent: format!("Bitwarden_CLI/{}", env!("CARGO_PKG_VERSION")),
174 device_type,
175 device_identifier: None,
177 bitwarden_client_version: Some(env!("CARGO_PKG_VERSION").to_string()),
178 bitwarden_package_type: Some("cli".to_string()),
179 });
180}