Skip to main content

systemprompt_cli/commands/cloud/
mod.rs

1//! `cloud` command tree for systemprompt.io Cloud.
2//!
3//! Routes [`CloudCommands`] to the auth, init, tenant, profile, deploy,
4//! backup, status, and dockerfile subcommands, and declares each command's
5//! profile/secret requirements via [`DescribeCommand`].
6//!
7//! Copyright (c) systemprompt.io — Business Source License 1.1.
8//! See <https://systemprompt.io> for licensing details.
9
10pub mod auth;
11pub mod backup;
12pub mod deploy;
13pub mod doctor;
14pub mod init;
15pub mod profile;
16mod status;
17pub mod templates;
18pub mod tenant;
19pub mod types;
20
21pub use systemprompt_cloud::{Environment, OAuthProvider};
22
23use crate::cli_settings::CliConfig;
24use crate::context::CommandContext;
25use crate::descriptor::{CommandDescriptor, DescribeCommand};
26use anyhow::Result;
27use clap::Subcommand;
28
29#[derive(Debug, Subcommand)]
30pub enum CloudCommands {
31    #[command(subcommand, about = "Authentication (login, logout, whoami)")]
32    Auth(auth::AuthCommands),
33
34    #[command(about = "Initialize project structure")]
35    Init {
36        #[arg(long)]
37        force: bool,
38    },
39
40    #[command(subcommand_required = false, about = "Manage tenants (local or cloud)")]
41    Tenant {
42        #[command(subcommand)]
43        command: Option<tenant::TenantCommands>,
44    },
45
46    #[command(subcommand_required = false, about = "Manage profiles")]
47    Profile {
48        #[command(subcommand)]
49        command: Option<profile::ProfileCommands>,
50    },
51
52    #[command(about = "Deploy to systemprompt.io Cloud")]
53    Deploy {
54        #[arg(long)]
55        skip_push: bool,
56
57        #[arg(long, short = 'p', help = "Profile name to deploy")]
58        profile: Option<String>,
59
60        #[arg(long, help = "Run the pre-deploy preflight only, without deploying")]
61        check: bool,
62    },
63
64    #[command(about = "Download the tenant's runtime services/ tree to a local directory")]
65    Backup {
66        #[arg(long, short = 'p', help = "Profile name to back up")]
67        profile: Option<String>,
68
69        #[arg(
70            long,
71            short = 'o',
72            help = "Output directory (default: ./systemprompt-backup-<timestamp>)"
73        )]
74        output: Option<std::path::PathBuf>,
75
76        #[arg(long, help = "List remote files without downloading")]
77        list: bool,
78    },
79
80    #[command(about = "Run the pre-deploy preflight for a profile without deploying")]
81    Doctor {
82        #[arg(long, short = 'p', help = "Profile name to check")]
83        profile: Option<String>,
84    },
85
86    #[command(about = "Check cloud deployment status")]
87    Status,
88
89    #[command(about = "Generate Dockerfile based on discovered extensions")]
90    Dockerfile,
91}
92
93impl DescribeCommand for CloudCommands {
94    fn descriptor(&self) -> CommandDescriptor {
95        match self {
96            Self::Deploy { .. } => CommandDescriptor::PROFILE_AND_SECRETS,
97            Self::Backup { .. } | Self::Status => CommandDescriptor::PROFILE_ONLY,
98            _ => CommandDescriptor::NONE,
99        }
100    }
101}
102
103pub async fn execute(cmd: CloudCommands, ctx: &CommandContext) -> Result<()> {
104    match cmd {
105        CloudCommands::Auth(cmd) => auth::execute(cmd, ctx).await,
106        CloudCommands::Init { force } => init::execute(force, &ctx.cli),
107        CloudCommands::Tenant { command } => tenant::execute(command, ctx).await,
108        CloudCommands::Profile { command } => profile::execute(command, ctx).await,
109        CloudCommands::Deploy {
110            skip_push,
111            profile,
112            check,
113        } => {
114            deploy::execute(
115                deploy::DeployArgs {
116                    skip_push,
117                    profile_name: profile,
118                    check,
119                },
120                ctx.prompter(),
121                &ctx.cli,
122            )
123            .await
124        },
125        CloudCommands::Backup {
126            profile,
127            output,
128            list,
129        } => {
130            backup::execute(
131                backup::BackupArgs {
132                    profile_name: profile,
133                    output,
134                    list,
135                },
136                ctx.prompter(),
137                &ctx.cli,
138            )
139            .await
140        },
141        CloudCommands::Doctor { profile } => {
142            doctor::execute(profile, ctx.prompter(), &ctx.cli).await
143        },
144        CloudCommands::Status => {
145            let result = status::execute(&ctx.cli).await?;
146            crate::shared::render_result(&result, &ctx.cli);
147            Ok(())
148        },
149        CloudCommands::Dockerfile => execute_dockerfile(&ctx.cli),
150    }
151}
152
153fn execute_dockerfile(config: &CliConfig) -> Result<()> {
154    use crate::shared::project::ProjectRoot;
155    use types::DockerfileOutput;
156
157    let project = ProjectRoot::discover().map_err(|e| anyhow::anyhow!("{}", e))?;
158    let content = systemprompt_cloud::deploy::generate_dockerfile_content(project.as_path());
159
160    let output = DockerfileOutput {
161        content: content.clone(),
162    };
163
164    if config.is_json_output() {
165        crate::shared::render_result(
166            &crate::shared::CommandOutput::copy_paste_titled("Dockerfile", output.content),
167            config,
168        );
169    } else {
170        systemprompt_logging::CliService::output(&content);
171    }
172
173    Ok(())
174}