systemprompt_cli/commands/cloud/backup/
mod.rs1mod client;
13
14use std::path::PathBuf;
15
16use anyhow::{Result, anyhow, bail};
17use systemprompt_loader::bundle::{ExtractOptions, TarLayout, extract_bytes};
18use systemprompt_logging::CliService;
19
20use client::BackupClient;
21
22use super::deploy::{resolve_deploy_target, resolve_profile};
23use crate::cli_settings::CliConfig;
24use crate::interactive::Prompter;
25
26const BACKUP_DIRS: &[&str] = &[
27 "agents", "skills", "content", "mcp", "ai", "config", "profiles",
28];
29
30const MAX_BACKUP_BYTES: u64 = 2 * 1024 * 1024 * 1024;
31
32pub(super) struct BackupArgs {
33 pub profile_name: Option<String>,
34 pub output: Option<PathBuf>,
35 pub list: bool,
36}
37
38pub(super) async fn execute(
39 args: BackupArgs,
40 prompter: &dyn Prompter,
41 config: &CliConfig,
42) -> Result<()> {
43 CliService::section("systemprompt.io Cloud Backup");
44
45 let (profile, _profile_path) = resolve_profile(prompter, args.profile_name.as_deref(), config)?;
46 if profile.target != systemprompt_models::ProfileType::Cloud {
47 bail!("Cannot back up a local profile. Select a cloud profile with --profile <name>.");
48 }
49
50 let target = resolve_deploy_target(&profile)?;
51 let hostname = target.hostname.ok_or_else(|| {
52 anyhow!(
53 "Tenant {} has no hostname. Run 'systemprompt cloud auth login' to refresh tenants.",
54 target.tenant_id
55 )
56 })?;
57
58 let spinner = CliService::spinner("Authenticating with tenant deployment...");
59 let client = BackupClient::connect(&hostname, target.creds.api_token.as_str()).await?;
60 spinner.finish_and_clear();
61
62 if args.list {
63 let manifest = client.fetch_manifest().await?;
64 CliService::info(&format!("{} files on {}", manifest.files.len(), hostname));
65 for file in &manifest.files {
66 CliService::info(&format!(" {} ({} bytes)", file.path, file.size));
67 }
68 return Ok(());
69 }
70
71 let output = args.output.unwrap_or_else(|| {
72 PathBuf::from(format!(
73 "systemprompt-backup-{}",
74 chrono::Utc::now().format("%Y%m%d-%H%M%S")
75 ))
76 });
77 std::fs::create_dir_all(&output)?;
78
79 let spinner = CliService::spinner("Downloading services tree...");
80 let bundle = client.download_bundle().await?;
81 spinner.finish_and_clear();
82
83 let count = extract_bytes(
84 &bundle,
85 &output,
86 &ExtractOptions {
87 allowed_dirs: BACKUP_DIRS,
88 max_bytes: MAX_BACKUP_BYTES,
89 layout: TarLayout::Root,
90 },
91 )?
92 .len();
93 CliService::success(&format!(
94 "Backed up {} files from {} to {}",
95 count,
96 hostname,
97 output.display()
98 ));
99
100 Ok(())
101}