systemprompt_cli/commands/cloud/auth/
mod.rs1pub mod admin_user;
12mod login;
13mod logout;
14mod whoami;
15
16use anyhow::anyhow;
17use systemprompt_logging::CliService;
18
19pub use login::{build_login_output, complete_login};
20
21use crate::context::CommandContext;
22use crate::shared::render_result;
23use anyhow::Result;
24use clap::{Args, Subcommand};
25
26use super::Environment;
27
28#[derive(Debug, Subcommand)]
29pub enum AuthCommands {
30 #[command(about = "Authenticate with systemprompt.io Cloud via OAuth")]
31 Login {
32 #[arg(value_enum, default_value_t = Environment::default(), hide = true)]
33 environment: Environment,
34 },
35
36 #[command(about = "Clear saved cloud credentials")]
37 Logout(LogoutArgs),
38
39 #[command(
40 about = "Show current authenticated user and token status",
41 alias = "status"
42 )]
43 Whoami,
44
45 #[command(about = "Sync cloud user as admin to local profile databases")]
46 AdminUser(AdminUserSyncArgs),
47}
48
49#[derive(Debug, Args)]
50pub struct AdminUserSyncArgs {
51 #[arg(short, long, help = "Show detailed discovery information")]
52 pub verbose: bool,
53
54 #[arg(long, help = "Specific profile to sync (default: all profiles)")]
55 pub profile: Option<String>,
56
57 #[arg(long, help = "Override database URL (requires --profile)")]
58 pub database_url: Option<String>,
59}
60
61#[derive(Debug, Clone, Copy, Args)]
62pub struct LogoutArgs {
63 #[arg(short = 'y', long, help = "Skip confirmation prompts")]
64 pub yes: bool,
65}
66
67pub async fn execute(cmd: AuthCommands, ctx: &CommandContext) -> Result<()> {
68 match cmd {
69 AuthCommands::Login { environment } => {
70 let result = login::execute(environment, ctx.prompter(), &ctx.cli).await?;
71 render_result(&result, &ctx.cli);
72 Ok(())
73 },
74 AuthCommands::Logout(args) => {
75 let result = logout::execute(args, ctx.prompter(), &ctx.cli).await?;
76 render_result(&result, &ctx.cli);
77 Ok(())
78 },
79 AuthCommands::Whoami => {
80 let result = whoami::execute(&ctx.cli).await?;
81 render_result(&result, &ctx.cli);
82 Ok(())
83 },
84 AuthCommands::AdminUser(args) => execute_admin_user_sync(args).await,
85 }
86}
87
88async fn execute_admin_user_sync(args: AdminUserSyncArgs) -> Result<()> {
89 CliService::section("Admin User Sync");
90
91 let cloud_user = admin_user::CloudUser::from_credentials()?
92 .ok_or_else(|| anyhow!("Not logged in. Run 'systemprompt cloud auth login' first."))?;
93
94 CliService::key_value("Cloud User", &cloud_user.email);
95
96 if let Some(profile_name) = &args.profile {
97 let database_url = if let Some(url) = &args.database_url {
98 url.clone()
99 } else {
100 let discovery = admin_user::discover_profiles()?;
101 discovery
102 .profiles
103 .into_iter()
104 .find(|p| &p.name == profile_name)
105 .and_then(|p| p.database_url)
106 .ok_or_else(|| {
107 anyhow!(
108 "Profile '{}' not found or has no database_url",
109 profile_name
110 )
111 })?
112 };
113
114 let result =
115 admin_user::sync_admin_to_database(&cloud_user, &database_url, profile_name).await;
116 admin_user::print_sync_results(&[result]);
117 } else {
118 if args.database_url.is_some() {
119 return Err(anyhow!("--database-url requires --profile"));
120 }
121
122 let results = admin_user::sync_admin_to_all_profiles(&cloud_user, args.verbose).await;
123 admin_user::print_sync_results(&results);
124 }
125
126 Ok(())
127}