totalreclaw_memory/
setup.rs1use serde::{Deserialize, Serialize};
8use std::path::PathBuf;
9
10use crate::embedding::EmbeddingMode;
11use crate::Result;
12
13pub fn config_dir() -> PathBuf {
19 dirs::home_dir()
20 .unwrap_or_else(|| PathBuf::from("."))
21 .join(".totalreclaw")
22}
23
24pub fn credentials_path() -> PathBuf {
26 config_dir().join("credentials.json")
27}
28
29pub fn embedding_config_path() -> PathBuf {
31 config_dir().join("embedding-config.json")
32}
33
34#[derive(Serialize, Deserialize)]
40pub struct Credentials {
41 pub recovery_phrase: String,
42}
43
44pub fn load_credentials() -> Result<Option<Credentials>> {
46 let path = credentials_path();
47 if !path.exists() {
48 return Ok(None);
49 }
50 let data = std::fs::read_to_string(&path)?;
51 let creds: Credentials =
52 serde_json::from_str(&data).map_err(|e| crate::Error::Crypto(e.to_string()))?;
53 Ok(Some(creds))
54}
55
56pub fn save_credentials(creds: &Credentials) -> Result<()> {
58 let dir = config_dir();
59 std::fs::create_dir_all(&dir)?;
60 let path = credentials_path();
61 let data = serde_json::to_string_pretty(creds)
62 .map_err(|e| crate::Error::Crypto(e.to_string()))?;
63 std::fs::write(&path, data)?;
64
65 #[cfg(unix)]
67 {
68 use std::os::unix::fs::PermissionsExt;
69 std::fs::set_permissions(&path, std::fs::Permissions::from_mode(0o600))?;
70 }
71
72 Ok(())
73}
74
75#[derive(Serialize, Deserialize)]
81pub struct EmbeddingConfig {
82 pub mode: String,
83 pub model: Option<String>,
84 pub dimensions: usize,
85 pub ollama_url: Option<String>,
86 pub provider_url: Option<String>,
87 pub api_key_env: Option<String>,
88}
89
90pub fn load_embedding_config() -> Result<Option<EmbeddingConfig>> {
92 let path = embedding_config_path();
93 if !path.exists() {
94 return Ok(None);
95 }
96 let data = std::fs::read_to_string(&path)?;
97 let config: EmbeddingConfig =
98 serde_json::from_str(&data).map_err(|e| crate::Error::Embedding(e.to_string()))?;
99 Ok(Some(config))
100}
101
102pub fn save_embedding_config(config: &EmbeddingConfig) -> Result<()> {
104 let dir = config_dir();
105 std::fs::create_dir_all(&dir)?;
106 let path = embedding_config_path();
107 let data = serde_json::to_string_pretty(config)
108 .map_err(|e| crate::Error::Embedding(e.to_string()))?;
109 std::fs::write(&path, data)?;
110 Ok(())
111}
112
113pub fn config_to_mode(config: &EmbeddingConfig) -> EmbeddingMode {
115 match config.mode.as_str() {
116 "local" => EmbeddingMode::Local {
117 model_path: config
118 .model
119 .clone()
120 .unwrap_or_else(|| "onnx-community/harrier-oss-v1-270m-ONNX".into()),
121 },
122 "ollama" => EmbeddingMode::Ollama {
123 base_url: config
124 .ollama_url
125 .clone()
126 .unwrap_or_else(|| "http://localhost:11434".into()),
127 model: config
128 .model
129 .clone()
130 .unwrap_or_else(|| "nomic-embed-text".into()),
131 },
132 "zeroclaw" => EmbeddingMode::ZeroClaw {
133 base_url: config
134 .provider_url
135 .clone()
136 .unwrap_or_default(),
137 api_key: std::env::var(
138 config
139 .api_key_env
140 .as_deref()
141 .unwrap_or("ZEROCLAW_EMBEDDING_API_KEY"),
142 )
143 .unwrap_or_default(),
144 },
145 "llm" | _ => EmbeddingMode::LlmProvider {
146 base_url: config
147 .provider_url
148 .clone()
149 .unwrap_or_else(|| "https://api.openai.com".into()),
150 api_key: std::env::var(
151 config
152 .api_key_env
153 .as_deref()
154 .unwrap_or("OPENAI_API_KEY"),
155 )
156 .unwrap_or_default(),
157 model: config
158 .model
159 .clone()
160 .unwrap_or_else(|| "text-embedding-3-small".into()),
161 },
162 }
163}
164
165pub fn generate_mnemonic() -> String {
167 use rand::RngCore;
169 let mut entropy = [0u8; 16];
170 rand::thread_rng().fill_bytes(&mut entropy);
171 let mnemonic = bip39::Mnemonic::from_entropy(&entropy)
172 .expect("Failed to generate mnemonic from 16 bytes");
173 mnemonic.to_string()
174}
175
176mod dirs {
181 use std::path::PathBuf;
182
183 pub fn home_dir() -> Option<PathBuf> {
184 std::env::var_os("HOME")
185 .or_else(|| std::env::var_os("USERPROFILE"))
186 .map(PathBuf::from)
187 }
188}