systemprompt_cli/commands/cloud/
mod.rs1pub mod auth;
2pub mod db;
3mod deploy;
4pub mod dockerfile;
5mod domain;
6mod init;
7pub mod profile;
8mod restart;
9mod secrets;
10mod status;
11pub mod sync;
12pub mod templates;
13pub mod tenant;
14
15pub use systemprompt_cloud::{Environment, OAuthProvider};
16
17use crate::cli_settings::CliConfig;
18use crate::descriptor::{CommandDescriptor, DescribeCommand};
19use anyhow::Result;
20use clap::Subcommand;
21
22#[derive(Debug, Subcommand)]
23pub enum CloudCommands {
24 #[command(subcommand, about = "Authentication (login, logout, whoami)")]
25 Auth(auth::AuthCommands),
26
27 #[command(about = "Initialize project structure")]
28 Init {
29 #[arg(long)]
30 force: bool,
31 },
32
33 #[command(subcommand_required = false, about = "Manage tenants (local or cloud)")]
34 Tenant {
35 #[command(subcommand)]
36 command: Option<tenant::TenantCommands>,
37 },
38
39 #[command(subcommand_required = false, about = "Manage profiles")]
40 Profile {
41 #[command(subcommand)]
42 command: Option<profile::ProfileCommands>,
43 },
44
45 #[command(about = "Deploy to systemprompt.io Cloud")]
46 Deploy {
47 #[arg(long)]
48 skip_push: bool,
49
50 #[arg(long, short = 'p', help = "Profile name to deploy")]
51 profile: Option<String>,
52
53 #[arg(
54 long,
55 help = "Skip pre-deploy sync from cloud (WARNING: may lose runtime files)"
56 )]
57 no_sync: bool,
58
59 #[arg(short = 'y', long, help = "Skip confirmation prompts")]
60 yes: bool,
61
62 #[arg(long, help = "Preview sync without deploying")]
63 dry_run: bool,
64 },
65
66 #[command(about = "Check cloud deployment status")]
67 Status,
68
69 #[command(about = "Restart tenant machine")]
70 Restart {
71 #[arg(long)]
72 tenant: Option<String>,
73
74 #[arg(short = 'y', long, help = "Skip confirmation prompts")]
75 yes: bool,
76 },
77
78 #[command(
79 subcommand_required = false,
80 about = "Sync between local and cloud environments"
81 )]
82 Sync {
83 #[command(subcommand)]
84 command: Option<sync::SyncCommands>,
85 },
86
87 #[command(subcommand, about = "Manage secrets for cloud tenant")]
88 Secrets(secrets::SecretsCommands),
89
90 #[command(about = "Generate Dockerfile based on discovered extensions")]
91 Dockerfile,
92
93 #[command(subcommand, about = "Cloud database operations")]
94 Db(db::CloudDbCommands),
95
96 #[command(subcommand, about = "Manage custom domain and TLS certificates")]
97 Domain(domain::DomainCommands),
98}
99
100impl DescribeCommand for CloudCommands {
101 fn descriptor(&self) -> CommandDescriptor {
102 match self {
103 Self::Sync {
104 command: Some(sync::SyncCommands::Local(_)),
105 } => CommandDescriptor::PROFILE_SECRETS_AND_PATHS,
106 Self::Deploy { .. } => CommandDescriptor::PROFILE_AND_SECRETS,
107 Self::Sync { command: Some(_) } | Self::Secrets { .. } => {
108 CommandDescriptor::PROFILE_AND_SECRETS
109 },
110 Self::Status | Self::Restart { .. } | Self::Domain { .. } => {
111 CommandDescriptor::PROFILE_ONLY
112 },
113 _ => CommandDescriptor::NONE,
114 }
115 }
116}
117
118impl CloudCommands {
119 pub const fn requires_profile(&self) -> bool {
120 matches!(
121 self,
122 Self::Sync { command: Some(_) }
123 | Self::Status
124 | Self::Restart { .. }
125 | Self::Secrets { .. }
126 | Self::Domain { .. }
127 )
128 }
129
130 pub const fn requires_secrets(&self) -> bool {
131 matches!(self, Self::Sync { command: Some(_) } | Self::Secrets { .. })
132 }
133}
134
135pub async fn execute(cmd: CloudCommands, config: &CliConfig) -> Result<()> {
136 match cmd {
137 CloudCommands::Auth(cmd) => auth::execute(cmd, config).await,
138 CloudCommands::Init { force } => init::execute(force, config),
139 CloudCommands::Tenant { command } => tenant::execute(command, config).await,
140 CloudCommands::Profile { command } => profile::execute(command, config).await,
141 CloudCommands::Deploy {
142 skip_push,
143 profile,
144 no_sync,
145 yes,
146 dry_run,
147 } => {
148 deploy::execute(
149 deploy::DeployArgs {
150 skip_push,
151 profile_name: profile,
152 no_sync,
153 yes,
154 dry_run,
155 },
156 config,
157 )
158 .await
159 },
160 CloudCommands::Status => status::execute().await,
161 CloudCommands::Restart { tenant, yes } => restart::execute(tenant, yes, config).await,
162 CloudCommands::Sync { command } => sync::execute(command, config).await,
163 CloudCommands::Secrets(cmd) => secrets::execute(cmd, config).await,
164 CloudCommands::Dockerfile => execute_dockerfile(),
165 CloudCommands::Db(cmd) => db::execute(cmd, config).await,
166 CloudCommands::Domain(cmd) => domain::execute(cmd, config).await,
167 }
168}
169
170fn execute_dockerfile() -> Result<()> {
171 use crate::shared::project::ProjectRoot;
172
173 let project = ProjectRoot::discover().map_err(|e| anyhow::anyhow!("{}", e))?;
174 dockerfile::print_dockerfile_suggestion(project.as_path());
175 Ok(())
176}