systemprompt_cli/commands/cloud/
mod.rs1pub 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 #[arg(
86 long,
87 help = "Also run the multi-replica checks: identity fingerprints, instance id, \
88 trusted proxies, write primary, replica lag, readiness"
89 )]
90 distributed: bool,
91 },
92
93 #[command(about = "Check cloud deployment status")]
94 Status,
95
96 #[command(about = "Generate Dockerfile based on discovered extensions")]
97 Dockerfile,
98}
99
100impl DescribeCommand for CloudCommands {
101 fn descriptor(&self) -> CommandDescriptor {
102 match self {
103 Self::Deploy { .. } => CommandDescriptor::PROFILE_AND_SECRETS,
104 Self::Backup { .. } | Self::Status => CommandDescriptor::PROFILE_ONLY,
105 _ => CommandDescriptor::NONE,
106 }
107 }
108}
109
110pub async fn execute(cmd: CloudCommands, ctx: &CommandContext) -> Result<()> {
111 match cmd {
112 CloudCommands::Auth(cmd) => auth::execute(cmd, ctx).await,
113 CloudCommands::Init { force } => init::execute(force, &ctx.cli),
114 CloudCommands::Tenant { command } => tenant::execute(command, ctx).await,
115 CloudCommands::Profile { command } => profile::execute(command, ctx).await,
116 CloudCommands::Deploy {
117 skip_push,
118 profile,
119 check,
120 } => {
121 deploy::execute(
122 deploy::DeployArgs {
123 skip_push,
124 profile_name: profile,
125 check,
126 },
127 ctx.prompter(),
128 &ctx.cli,
129 )
130 .await
131 },
132 CloudCommands::Backup {
133 profile,
134 output,
135 list,
136 } => {
137 backup::execute(
138 backup::BackupArgs {
139 profile_name: profile,
140 output,
141 list,
142 },
143 ctx.prompter(),
144 &ctx.cli,
145 )
146 .await
147 },
148 CloudCommands::Doctor {
149 profile,
150 distributed,
151 } => doctor::execute(profile, distributed, ctx.prompter(), &ctx.cli).await,
152 CloudCommands::Status => {
153 let result = status::execute(&ctx.cli).await?;
154 crate::shared::render_result(&result, &ctx.cli);
155 Ok(())
156 },
157 CloudCommands::Dockerfile => execute_dockerfile(&ctx.cli),
158 }
159}
160
161fn execute_dockerfile(config: &CliConfig) -> Result<()> {
162 use crate::shared::project::ProjectRoot;
163 use types::DockerfileOutput;
164
165 let project = ProjectRoot::discover().map_err(|e| anyhow::anyhow!("{}", e))?;
166 let content = systemprompt_cloud::deploy::generate_dockerfile_content(project.as_path());
167
168 let output = DockerfileOutput {
169 content: content.clone(),
170 };
171
172 if config.is_json_output() {
173 crate::shared::render_result(
174 &crate::shared::CommandOutput::copy_paste_titled("Dockerfile", output.content),
175 config,
176 );
177 } else {
178 systemprompt_logging::CliService::output(&content);
179 }
180
181 Ok(())
182}