//! `relicario add ` — create a new item of the given type. //! //! `cmd_add` resolves `title` / non-secret prompts, then delegates to the //! shared builders in `commands/item_build.rs`. Group / tags / favorite are //! set AFTER the build so the builders stay portable to the org vault. use anyhow::Result; use crate::AddKind; use crate::commands::item_build as ib; use crate::prompt::{prompt_or_flag, prompt_or_flag_optional}; pub fn cmd_add(kind: AddKind) -> Result<()> { let vault = crate::session::UnlockedVault::unlock_interactive()?; let mut manifest = vault.load_manifest()?; let item = match kind { AddKind::Login { title, username, url, password_prompt, password, password_stdin, group, tags, favorite, totp_qr } => { let title = prompt_or_flag(title, "Title", |s| Ok(s.to_string()))?; let username = prompt_or_flag_optional(username, "Username", |s| Ok(s.to_string()))?; let url = prompt_or_flag_optional(url, "URL", |s| Ok(s.to_string()))?; let mut item = ib::build_login(title, username, url, password, password_stdin, password_prompt, totp_qr)?; item.group = group; item.tags = tags; item.favorite = favorite; item } AddKind::SecureNote { title, body_stdin, group, tags } => { // Per the v0.8.1 spec's unified secret model, a note body is a // multiline secret that always reads stdin to EOF. `body_stdin=false` // means "print the Ctrl-D hint" (interactive default); `true` suppresses // the hint for non-interactive use. // Secret-resolution rule: `commands/item_build.rs` `resolve_secret_multiline`. let title = prompt_or_flag(title, "Title", |s| Ok(s.to_string()))?; let mut item = ib::build_secure_note(title, None, body_stdin)?; item.group = group; item.tags = tags; item } AddKind::Identity { title, full_name, email, phone, date_of_birth, group, tags } => { let title = prompt_or_flag(title, "Title", |s| Ok(s.to_string()))?; let mut item = ib::build_identity(title, full_name, email, phone, date_of_birth)?; item.group = group; item.tags = tags; item } AddKind::Card { title, holder, expiry, kind, number_stdin, cvv_stdin, pin_stdin, group, tags } => { let title = prompt_or_flag(title, "Title", |s| Ok(s.to_string()))?; let mut item = ib::build_card(title, holder, expiry, &kind, number_stdin, cvv_stdin, pin_stdin)?; item.group = group; item.tags = tags; item } AddKind::Key { title, label, algorithm, material_stdin, group, tags } => { // public_key is None for the personal vault: the legacy `prompt_optional` // for it was unreachable (stdin already at EOF after the key-material read). // Org `add key` (Dev-B) supplies it via --public-key. let title = prompt_or_flag(title, "Title", |s| Ok(s.to_string()))?; let mut item = ib::build_key(title, label, algorithm, None, material_stdin)?; item.group = group; item.tags = tags; item } AddKind::Document { title, file, group, tags } => { let title = prompt_or_flag(title, "Title", |s| Ok(s.to_string()))?; let caps = vault.load_settings()?.attachment_caps; let (mut item, enc) = ib::build_document(title, file, vault.key(), caps.per_attachment_max_bytes)?; item.group = group; item.tags = tags; let att_dir = vault.root().join("attachments").join(item.id.as_str()); std::fs::create_dir_all(&att_dir)?; std::fs::write(att_dir.join(format!("{}.enc", enc.id.as_str())), &enc.bytes)?; item } AddKind::Totp { title, issuer, label, secret, secret_stdin, period, digits, algorithm, group, tags } => { let title = prompt_or_flag(title, "Title", |s| Ok(s.to_string()))?; let mut item = ib::build_totp(title, issuer, label, secret, secret_stdin, period, digits, &algorithm)?; item.group = group; item.tags = tags; item } }; vault.save_item(&item)?; manifest.upsert(&item); vault.after_manifest_change(&manifest)?; let mut paths: Vec = vec![ format!("items/{}.enc", item.id.as_str()), "manifest.enc".into(), ]; for att in &item.attachments { paths.push(format!("attachments/{}/{}.enc", item.id.as_str(), att.id.as_str())); } let path_refs: Vec<&str> = paths.iter().map(|s| s.as_str()).collect(); super::commit_paths(&vault, &format!("add: {} ({})", crate::helpers::sanitize_for_commit(&item.title), item.id.as_str()), &path_refs)?; eprintln!("Added: {} (id={})", item.title, item.id.as_str()); Ok(()) }