-
Notifications
You must be signed in to change notification settings - Fork 361
fix: keep setup mode usable when provider credentials are unavailable #422
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -348,7 +348,13 @@ pub(super) async fn get_providers( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) -> Result<Json<ProvidersResponse>, StatusCode> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let config_path = state.config_path.read().await.clone(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let instance_dir = (**state.instance_dir.load()).clone(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let secrets_store = state.secrets_store.load(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let openai_oauth_configured = crate::openai_auth::credentials_path(&instance_dir).exists(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let env_set = |name: &str| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| std::env::var(name) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .ok() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .is_some_and(|value| !value.trim().is_empty()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| anthropic, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -381,17 +387,39 @@ pub(super) async fn get_providers( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .parse() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let resolve_value = |value: &str| -> Option<String> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if let Some(alias) = value.strip_prefix("secret:") { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let store = secrets_store.as_ref().as_ref()?; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return match store.get(alias) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Ok(secret) => Some(secret.expose().to_string()), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Err(error) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| tracing::warn!(%error, alias, "failed to resolve secret reference"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| None | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if let Some(var_name) = value.strip_prefix("env:") { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return std::env::var(var_name) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .ok() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .filter(|resolved| !resolved.trim().is_empty()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if value.trim().is_empty() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| None | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Some(value.to_string()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let has_value = |key: &str, env_var: &str| -> bool { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if let Some(llm) = doc.get("llm") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| && let Some(val) = llm.get(key) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| && let Some(s) = val.as_str() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if let Some(var_name) = s.strip_prefix("env:") { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return std::env::var(var_name).is_ok(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return !s.is_empty(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return resolve_value(s).is_some(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+390
to
421
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| std::env::var(env_var).is_ok() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| env_set(env_var) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -421,28 +449,28 @@ pub(super) async fn get_providers( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| std::env::var("ANTHROPIC_API_KEY").is_ok(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| std::env::var("OPENAI_API_KEY").is_ok(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| env_set("ANTHROPIC_API_KEY"), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| env_set("OPENAI_API_KEY"), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| openai_oauth_configured, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| std::env::var("OPENROUTER_API_KEY").is_ok(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| std::env::var("KILO_API_KEY").is_ok(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| std::env::var("ZHIPU_API_KEY").is_ok(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| std::env::var("GROQ_API_KEY").is_ok(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| std::env::var("TOGETHER_API_KEY").is_ok(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| std::env::var("FIREWORKS_API_KEY").is_ok(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| std::env::var("DEEPSEEK_API_KEY").is_ok(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| std::env::var("XAI_API_KEY").is_ok(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| std::env::var("MISTRAL_API_KEY").is_ok(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| std::env::var("GEMINI_API_KEY").is_ok(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| std::env::var("OLLAMA_BASE_URL").is_ok() || std::env::var("OLLAMA_API_KEY").is_ok(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| std::env::var("OPENCODE_ZEN_API_KEY").is_ok(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| std::env::var("OPENCODE_GO_API_KEY").is_ok(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| std::env::var("NVIDIA_API_KEY").is_ok(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| std::env::var("MINIMAX_API_KEY").is_ok(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| std::env::var("MINIMAX_CN_API_KEY").is_ok(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| std::env::var("MOONSHOT_API_KEY").is_ok(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| std::env::var("ZAI_CODING_PLAN_API_KEY").is_ok(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| std::env::var("GITHUB_COPILOT_API_KEY").is_ok(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| env_set("OPENROUTER_API_KEY"), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| env_set("KILO_API_KEY"), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| env_set("ZHIPU_API_KEY"), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| env_set("GROQ_API_KEY"), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| env_set("TOGETHER_API_KEY"), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| env_set("FIREWORKS_API_KEY"), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| env_set("DEEPSEEK_API_KEY"), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| env_set("XAI_API_KEY"), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| env_set("MISTRAL_API_KEY"), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| env_set("GEMINI_API_KEY"), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| env_set("OLLAMA_BASE_URL") || env_set("OLLAMA_API_KEY"), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| env_set("OPENCODE_ZEN_API_KEY"), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| env_set("OPENCODE_GO_API_KEY"), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| env_set("NVIDIA_API_KEY"), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| env_set("MINIMAX_API_KEY"), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| env_set("MINIMAX_CN_API_KEY"), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| env_set("MOONSHOT_API_KEY"), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| env_set("ZAI_CODING_PLAN_API_KEY"), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| env_set("GITHUB_COPILOT_API_KEY"), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -44,8 +44,10 @@ pub async fn secrets_status(State(state): State<Arc<ApiState>>) -> impl IntoResp | |||||||||
| Err(e) => return e.into_response(), | ||||||||||
| }; | ||||||||||
|
|
||||||||||
| // TODO: detect platform_managed from deployment mode. | ||||||||||
| match store.status(false) { | ||||||||||
| let platform_managed = std::env::var("SPACEBOT_DEPLOYMENT") | ||||||||||
| .is_ok_and(|deployment| deployment.eq_ignore_ascii_case("hosted")); | ||||||||||
|
Comment on lines
+47
to
+48
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Minor: probably worth trimming here to avoid treating
Suggested change
|
||||||||||
|
|
||||||||||
| match store.status(platform_managed) { | ||||||||||
| Ok(status) => Json(status).into_response(), | ||||||||||
| Err(error) => ( | ||||||||||
| StatusCode::INTERNAL_SERVER_ERROR, | ||||||||||
|
|
||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -1344,31 +1344,91 @@ fn bootstrap_secrets_store( | |||||||||||||||||||||||||||||||
| // Try to auto-unlock if encrypted. | ||||||||||||||||||||||||||||||||
| if store.is_encrypted() { | ||||||||||||||||||||||||||||||||
| let keystore = spacebot::secrets::keystore::platform_keystore(); | ||||||||||||||||||||||||||||||||
| let tmpfs_paths = [ | ||||||||||||||||||||||||||||||||
| std::path::Path::new("/run/spacebot/master_key"), | ||||||||||||||||||||||||||||||||
| std::path::Path::new("/run/secrets/master_key"), | ||||||||||||||||||||||||||||||||
| ]; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| // Hosted: check tmpfs-injected key. | ||||||||||||||||||||||||||||||||
| let tmpfs_key_path = std::path::Path::new("/run/spacebot/master_key"); | ||||||||||||||||||||||||||||||||
| let master_key = if tmpfs_key_path.exists() { | ||||||||||||||||||||||||||||||||
| std::fs::read(tmpfs_key_path).ok().inspect(|key| { | ||||||||||||||||||||||||||||||||
| if let Err(error) = std::fs::remove_file(tmpfs_key_path) { | ||||||||||||||||||||||||||||||||
| tracing::warn!(%error, "failed to remove tmpfs master key — key may remain accessible"); | ||||||||||||||||||||||||||||||||
| let tmpfs_master_key = tmpfs_paths.iter().find_map(|path| { | ||||||||||||||||||||||||||||||||
| if !path.exists() { | ||||||||||||||||||||||||||||||||
| return None; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| let raw_key = match std::fs::read(path) { | ||||||||||||||||||||||||||||||||
| Ok(key) => key, | ||||||||||||||||||||||||||||||||
| Err(error) => { | ||||||||||||||||||||||||||||||||
| tracing::warn!(%error, path = %path.display(), "failed to read tmpfs master key"); | ||||||||||||||||||||||||||||||||
| return None; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
Comment on lines
+1360
to
1363
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If reading the tmpfs key fails (for both paths), the injected key file(s) will be left on disk. Consider a best-effort remove on the error path too.
Suggested change
|
||||||||||||||||||||||||||||||||
| if let Err(error) = keystore.store_key(KEYSTORE_INSTANCE_ID, key) { | ||||||||||||||||||||||||||||||||
| tracing::warn!(%error, "failed to persist master key to OS credential store"); | ||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| // Platform currently stores keys as 64-char hex strings. Decode | ||||||||||||||||||||||||||||||||
| // those to raw bytes before unlock; otherwise treat as raw bytes. | ||||||||||||||||||||||||||||||||
| if let Ok(text) = std::str::from_utf8(&raw_key) { | ||||||||||||||||||||||||||||||||
| let trimmed = text.trim(); | ||||||||||||||||||||||||||||||||
| if trimmed.len() == 64 && trimmed.bytes().all(|byte| byte.is_ascii_hexdigit()) { | ||||||||||||||||||||||||||||||||
| return match hex::decode(trimmed) { | ||||||||||||||||||||||||||||||||
| Ok(decoded) => Some(decoded), | ||||||||||||||||||||||||||||||||
| Err(error) => { | ||||||||||||||||||||||||||||||||
| tracing::warn!( | ||||||||||||||||||||||||||||||||
| %error, | ||||||||||||||||||||||||||||||||
| path = %path.display(), | ||||||||||||||||||||||||||||||||
| "failed to decode hex tmpfs master key, falling back to raw bytes" | ||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||
| Some(raw_key) | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| Some(raw_key) | ||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| let mut unlocked = false; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| if let Some(key) = tmpfs_master_key { | ||||||||||||||||||||||||||||||||
| match store.unlock(&key) { | ||||||||||||||||||||||||||||||||
| Ok(()) => { | ||||||||||||||||||||||||||||||||
| unlocked = true; | ||||||||||||||||||||||||||||||||
| if let Err(error) = keystore.store_key(KEYSTORE_INSTANCE_ID, &key) { | ||||||||||||||||||||||||||||||||
| tracing::warn!(%error, "failed to persist master key to OS credential store"); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| // Clean up tmpfs key files only after a successful unlock. | ||||||||||||||||||||||||||||||||
| for cleanup_path in tmpfs_paths { | ||||||||||||||||||||||||||||||||
| if cleanup_path.exists() | ||||||||||||||||||||||||||||||||
| && let Err(error) = std::fs::remove_file(cleanup_path) | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| tracing::warn!( | ||||||||||||||||||||||||||||||||
| %error, | ||||||||||||||||||||||||||||||||
| path = %cleanup_path.display(), | ||||||||||||||||||||||||||||||||
| "failed to remove tmpfs master key — key may remain accessible" | ||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| Err(error) => { | ||||||||||||||||||||||||||||||||
| tracing::warn!(%error, "failed to unlock secret store with tmpfs key"); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
Comment on lines
+1353
to
+1414
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Try all tmpfs keys before deleting them. The cleanup happens before the first 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| if !unlocked { | ||||||||||||||||||||||||||||||||
| // Try instance-level key first, then fall back to legacy agent keys. | ||||||||||||||||||||||||||||||||
| keystore | ||||||||||||||||||||||||||||||||
| let master_key = keystore | ||||||||||||||||||||||||||||||||
| .load_key(KEYSTORE_INSTANCE_ID) | ||||||||||||||||||||||||||||||||
| .ok() | ||||||||||||||||||||||||||||||||
| .flatten() | ||||||||||||||||||||||||||||||||
| .or_else(|| load_legacy_keystore_key(&instance_dir)) | ||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||
| .or_else(|| load_legacy_keystore_key(&instance_dir)); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| if let Some(key) = master_key | ||||||||||||||||||||||||||||||||
| && let Err(error) = store.unlock(&key) | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| tracing::warn!(%error, "failed to unlock secret store — secrets will be inaccessible"); | ||||||||||||||||||||||||||||||||
| if let Some(key) = master_key | ||||||||||||||||||||||||||||||||
| && let Err(error) = store.unlock(&key) | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| tracing::warn!( | ||||||||||||||||||||||||||||||||
| %error, | ||||||||||||||||||||||||||||||||
| "failed to unlock secret store — secrets will be inaccessible" | ||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
|
|
@@ -1475,6 +1535,25 @@ fn has_provider_credentials( | |||||||||||||||||||||||||||||||
| || spacebot::openai_auth::credentials_path(instance_dir).exists() | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| fn configured_agent_infos(config: &spacebot::config::Config) -> Vec<spacebot::api::AgentInfo> { | ||||||||||||||||||||||||||||||||
| config | ||||||||||||||||||||||||||||||||
| .resolve_agents() | ||||||||||||||||||||||||||||||||
| .into_iter() | ||||||||||||||||||||||||||||||||
| .map(|agent| spacebot::api::AgentInfo { | ||||||||||||||||||||||||||||||||
| id: agent.id, | ||||||||||||||||||||||||||||||||
| display_name: agent.display_name, | ||||||||||||||||||||||||||||||||
| role: agent.role, | ||||||||||||||||||||||||||||||||
| gradient_start: agent.gradient_start, | ||||||||||||||||||||||||||||||||
| gradient_end: agent.gradient_end, | ||||||||||||||||||||||||||||||||
| workspace: agent.workspace, | ||||||||||||||||||||||||||||||||
| context_window: agent.context_window, | ||||||||||||||||||||||||||||||||
| max_turns: agent.max_turns, | ||||||||||||||||||||||||||||||||
| max_concurrent_branches: agent.max_concurrent_branches, | ||||||||||||||||||||||||||||||||
| max_concurrent_workers: agent.max_concurrent_workers, | ||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||
| .collect() | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| async fn run( | ||||||||||||||||||||||||||||||||
| config: spacebot::config::Config, | ||||||||||||||||||||||||||||||||
| foreground: bool, | ||||||||||||||||||||||||||||||||
|
|
@@ -1519,6 +1598,12 @@ async fn run( | |||||||||||||||||||||||||||||||
| api_state.auth_token = config.api.auth_token.clone(); | ||||||||||||||||||||||||||||||||
| let api_state = Arc::new(api_state); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| // Keep the secrets API available in setup mode so encrypted stores can be | ||||||||||||||||||||||||||||||||
| // unlocked before providers/agents are initialized. | ||||||||||||||||||||||||||||||||
| if let Some(store) = &bootstrapped_store { | ||||||||||||||||||||||||||||||||
| api_state.set_secrets_store(store.clone()); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| // Start background update checker | ||||||||||||||||||||||||||||||||
| spacebot::update::spawn_update_checker(api_state.update_status.clone()); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
|
|
@@ -1637,6 +1722,7 @@ async fn run( | |||||||||||||||||||||||||||||||
| api_state.set_agent_links((**agent_links.load()).clone()); | ||||||||||||||||||||||||||||||||
| api_state.set_agent_groups(config.groups.clone()); | ||||||||||||||||||||||||||||||||
| api_state.set_agent_humans(config.humans.clone()); | ||||||||||||||||||||||||||||||||
| api_state.set_agent_configs(configured_agent_infos(&config)); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| // Track whether agents have been initialized | ||||||||||||||||||||||||||||||||
| let mut agents_initialized = false; | ||||||||||||||||||||||||||||||||
|
|
@@ -2217,9 +2303,10 @@ async fn run( | |||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| match new_config { | ||||||||||||||||||||||||||||||||
| Ok(new_config) | ||||||||||||||||||||||||||||||||
| if has_provider_credentials(&new_config.llm, &new_config.instance_dir) => | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| Ok(new_config) => { | ||||||||||||||||||||||||||||||||
| api_state.set_agent_configs(configured_agent_infos(&new_config)); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| if has_provider_credentials(&new_config.llm, &new_config.instance_dir) { | ||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks like the body of this |
||||||||||||||||||||||||||||||||
| // Refresh in-memory defaults so newly created agents | ||||||||||||||||||||||||||||||||
| // inherit the latest routing from the updated config. | ||||||||||||||||||||||||||||||||
| api_state.set_defaults_config(new_config.defaults.clone()).await; | ||||||||||||||||||||||||||||||||
|
|
@@ -2296,9 +2383,9 @@ async fn run( | |||||||||||||||||||||||||||||||
| tracing::error!(%error, "failed to create LLM manager with new keys"); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| Ok(_) => { | ||||||||||||||||||||||||||||||||
| tracing::warn!("config reloaded but still no providers configured"); | ||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||
| tracing::warn!("config reloaded but still no providers configured"); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| Err(error) => { | ||||||||||||||||||||||||||||||||
| tracing::error!(%error, "failed to reload config after provider setup"); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: spacedriveapp/spacebot
Length of output: 5004
🏁 Script executed:
Repository: spacedriveapp/spacebot
Length of output: 2396
Environment variables should handle missing/invalid values explicitly instead of silently discarding errors with
.ok().Lines 353-357 and 403-405 use
.ok()onstd::env::var(), which suppresses error context (e.g., invalid UTF-8). Per coding guidelines, errors must be handled, logged, or propagated—.ok()is only permitted on channel sends where the receiver may be dropped.🤖 Prompt for AI Agents