systemprompt_cli/commands/core/services/
keygen.rs1use std::path::PathBuf;
7
8use anyhow::{Context, Result};
9use clap::Args;
10use serde::Serialize;
11
12use super::signing::BundleSigningKey;
13use crate::shared::CommandOutput;
14
15#[derive(Debug, Clone, Args)]
16pub struct KeygenArgs {
17 #[arg(
18 long,
19 help = "Write the base64 seed to this file instead of printing it"
20 )]
21 pub out: Option<PathBuf>,
22}
23
24#[derive(Debug, Serialize)]
25pub struct KeygenOutcome {
26 pub public_key: String,
27 pub key_id: String,
28 pub seed: Option<String>,
29 pub seed_file: Option<String>,
30}
31
32pub fn execute(args: &KeygenArgs) -> Result<CommandOutput> {
33 let key = BundleSigningKey::generate();
34 let outcome = write_key(&key, args.out.as_deref())?;
35 Ok(CommandOutput::card_value("Bundle Signing Key", &outcome))
36}
37
38fn write_key(key: &BundleSigningKey, out: Option<&std::path::Path>) -> Result<KeygenOutcome> {
39 let mut outcome = KeygenOutcome {
40 public_key: key.public_key.clone(),
41 key_id: key.key_id.clone(),
42 seed: None,
43 seed_file: None,
44 };
45
46 match out {
47 Some(path) => {
48 if let Some(parent) = path.parent().filter(|p| !p.as_os_str().is_empty()) {
49 std::fs::create_dir_all(parent)
50 .with_context(|| format!("Failed to create {}", parent.display()))?;
51 }
52 std::fs::write(path, key.seed_b64())
53 .with_context(|| format!("Failed to write {}", path.display()))?;
54 outcome.seed_file = Some(path.display().to_string());
55 },
56 None => outcome.seed = Some(key.seed_b64()),
57 }
58 Ok(outcome)
59}