Skip to content

Commit

Permalink
latest
Browse files Browse the repository at this point in the history
  • Loading branch information
lily-de committed Feb 11, 2025
1 parent 88a9edb commit ff6ea5c
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 78 deletions.
154 changes: 83 additions & 71 deletions crates/goose-server/src/routes/config_management.rs
Original file line number Diff line number Diff line change
@@ -1,39 +1,23 @@
use axum::{routing::{get, post, delete}, Json, http::StatusCode, Router, extract::{Query, State}};
use http::StatusCode;
use axum::{
extract::State,
routing::{delete, get, post},
Json, Router,
};
use goose::config::Config;
use serde::{Deserialize, Serialize};
use serde_yaml::Value;
use serde_json::Value;
use std::{collections::HashMap, sync::Arc};
use tokio::sync::Mutex;
use crate::{state::AppState, config_manager::ConfigManager};
use utoipa::ToSchema;
use tracing;
use once_cell::sync::Lazy;

static CONFIG_MANAGER: Lazy<ConfigManager> = Lazy::new(|| {
ConfigManager::new(
"goose",
dirs::home_dir()
.expect("goose requires a home dir")
.join(".config")
.join("goose")
.to_str()
.unwrap(),
).expect("Failed to initialize ConfigManager")
});


fn config_manager() -> ConfigManager {
let config_dir = dirs::home_dir()
.expect("goose requires a home dir")
.join(".config")
.join("goose");
ConfigManager::new("goose", config_dir.to_str().unwrap())
}
use utoipa::ToSchema;

use crate::state::AppState;

#[derive(Deserialize, ToSchema)]
pub struct UpsertConfigQuery {
pub key: String,
pub value: Value,
#[allow(dead_code)]
pub is_secret: Option<bool>,
}

Expand Down Expand Up @@ -63,16 +47,21 @@ pub struct ConfigResponse {
)
)]
pub async fn upsert_config(
State(state): State<Arc<Mutex<HashMap<String, Value>>>>,
Json(query): Json<UpsertConfigQuery>
State(_state): State<Arc<Mutex<HashMap<String, Value>>>>,
Json(query): Json<UpsertConfigQuery>,
) -> Result<Json<Value>, StatusCode> {
let mut config = state.lock().await;
let config = Config::global();

// Use ConfigManager to persist config
config_manager().set(&query.key, query.value.clone()).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
let result = if query.is_secret.unwrap_or(false) {
config.set_secret(&query.key, query.value)
} else {
config.set(&query.key, query.value)
};

config.insert(query.key.clone(), query.value);
Ok(Json(Value::String(format!("Upserted key {}", query.key))))
match result {
Ok(_) => Ok(Json(Value::String(format!("Upserted key {}", query.key)))),
Err(_) => Err(StatusCode::INTERNAL_SERVER_ERROR),
}
}

#[utoipa::path(
Expand All @@ -86,15 +75,15 @@ pub async fn upsert_config(
)
)]
pub async fn remove_config(
State(state): State<Arc<Mutex<HashMap<String, Value>>>>,
Json(query): Json<ConfigKeyQuery>
State(_state): State<Arc<Mutex<HashMap<String, Value>>>>,
Json(query): Json<ConfigKeyQuery>,
) -> Result<Json<String>, StatusCode> {
if config_manager().delete(&query.key).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)? {
let mut config = state.lock().await;
config.remove(&query.key);
return Ok(Json(format!("Removed key {}", query.key)));
let config = Config::global();

match config.delete(&query.key) {
Ok(_) => Ok(Json(format!("Removed key {}", query.key))),
Err(_) => Err(StatusCode::NOT_FOUND),
}
Err(StatusCode::NOT_FOUND)
}

#[utoipa::path(
Expand All @@ -107,13 +96,15 @@ pub async fn remove_config(
)
)]
pub async fn read_config(
State(state): State<Arc<Mutex<HashMap<String, Value>>>>,
Json(query): Json<ConfigKeyQuery>
State(_state): State<Arc<Mutex<HashMap<String, Value>>>>,
Json(query): Json<ConfigKeyQuery>,
) -> Result<Json<Value>, StatusCode> {
if let Ok(value) = config_manager().get(&query.key) {
return Ok(Json(value))
let config = Config::global();

match config.get::<Value>(&query.key) {
Ok(value) => Ok(Json(value)),
Err(_) => Err(StatusCode::NOT_FOUND),
}
Err(StatusCode::NOT_FOUND)
}

#[utoipa::path(
Expand All @@ -127,18 +118,24 @@ pub async fn read_config(
)
)]
pub async fn add_extension(
State(state): State<Arc<Mutex<HashMap<String, Value>>>>,
Json(extension): Json<ExtensionQuery>
State(_state): State<Arc<Mutex<HashMap<String, Value>>>>,
Json(extension): Json<ExtensionQuery>,
) -> Result<Json<String>, StatusCode> {
let mut config = state.lock().await;
if let Some(extensions) = config.get_mut("extensions") {
if let Value::Mapping(map) = extensions {
map.insert(Value::String(extension.name.clone()), extension.config);
config_manager().set(&format!("extensions.{}", extension.name), map.clone()).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
return Ok(Json(format!("Added extension {}", extension.name)));
}
let config = Config::global();

// Get current extensions or initialize empty map
let mut extensions: HashMap<String, Value> = config
.get("extensions")
.unwrap_or_else(|_| HashMap::new());

// Add new extension
extensions.insert(extension.name.clone(), extension.config);

// Save updated extensions
match config.set("extensions", Value::Object(extensions.into_iter().collect())) {
Ok(_) => Ok(Json(format!("Added extension {}", extension.name))),
Err(_) => Err(StatusCode::INTERNAL_SERVER_ERROR),
}
Err(StatusCode::BAD_REQUEST)
}

#[utoipa::path(
Expand All @@ -152,19 +149,27 @@ pub async fn add_extension(
)
)]
pub async fn remove_extension(
State(state): State<Arc<Mutex<HashMap<String, Value>>>>,
Json(query): Json<ConfigKeyQuery>
State(_state): State<Arc<Mutex<HashMap<String, Value>>>>,
Json(query): Json<ConfigKeyQuery>,
) -> Result<Json<String>, StatusCode> {
let mut config = state.lock().await;
if let Some(extensions) = config.get_mut("extensions") {
if let Value::Mapping(map) = extensions {
if map.remove(&Value::String(query.key.clone())).is_some() {
config_manager().delete(&format!("extensions.{}", query.key)).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
return Ok(Json(format!("Removed extension {}", query.key)));
}
let config = Config::global();

// Get current extensions
let mut extensions: HashMap<String, Value> = match config.get("extensions") {
Ok(exts) => exts,
Err(_) => return Err(StatusCode::NOT_FOUND),
};

// Remove extension if it exists
if extensions.remove(&query.key).is_some() {
// Save updated extensions
match config.set("extensions", Value::Object(extensions.into_iter().collect())) {
Ok(_) => Ok(Json(format!("Removed extension {}", query.key))),
Err(_) => Err(StatusCode::INTERNAL_SERVER_ERROR),
}
} else {
Err(StatusCode::NOT_FOUND)
}
Err(StatusCode::NOT_FOUND)
}

#[utoipa::path(
Expand All @@ -175,10 +180,17 @@ pub async fn remove_extension(
)
)]
pub async fn read_all_config(
State(state): State<Arc<Mutex<HashMap<String, Value>>>>
) -> Json<ConfigResponse> {
let config = config_manager().get_all().unwrap_or_default();
Json(ConfigResponse { config })
State(_state): State<Arc<Mutex<HashMap<String, Value>>>>,
) -> Result<Json<ConfigResponse>, StatusCode> {
let config = Config::global();

// Load values from config file
let values = match config.load_values() {
Ok(v) => v,
Err(_) => HashMap::new(),
};

Ok(Json(ConfigResponse { config: values }))
}

pub fn routes(state: AppState) -> Router {
Expand All @@ -190,4 +202,4 @@ pub fn routes(state: AppState) -> Router {
.route("/config/extension", post(add_extension))
.route("/config/extension", delete(remove_extension))
.with_state(state.config)
}
}
4 changes: 1 addition & 3 deletions crates/goose/src/config/base.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use super::config_manager::ConfigManager;
use serde::Deserialize;
use keyring::Entry;
use once_cell::sync::OnceCell;
use serde::Deserialize;
Expand Down Expand Up @@ -151,7 +149,7 @@ impl Config {
}

// Load current values from the config file
fn load_values(&self) -> Result<HashMap<String, Value>, ConfigError> {
pub fn load_values(&self) -> Result<HashMap<String, Value>, ConfigError> {
if self.config_path.exists() {
let file_content = std::fs::read_to_string(&self.config_path)?;
// Parse YAML into JSON Value for consistent internal representation
Expand Down
2 changes: 1 addition & 1 deletion crates/goose/src/config/extensions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,4 +115,4 @@ impl ExtensionManager {
}
fn get_keys(entries: HashMap<String, ExtensionEntry>) -> Vec<String> {
entries.into_keys().collect()
}
}
4 changes: 1 addition & 3 deletions crates/goose/src/config/mod.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
mod base;
mod extensions;
mod config_manager;

pub use crate::agents::ExtensionConfig;
pub use base::{Config};
pub use base::{Config, ConfigError};
pub use extensions::{ExtensionEntry, ExtensionManager};
pub use config_manager::{ConfigManager, ConfigError};

0 comments on commit ff6ea5c

Please sign in to comment.