mirror of
https://github.com/aljazceru/goose.git
synced 2025-12-22 08:34:21 +01:00
feat: follow XDG spec on linux/mac and use windows known folders for config and logs (#1153)
This commit is contained in:
@@ -27,7 +27,7 @@ tokio = { version = "1.0", features = ["full"] }
|
|||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
serde = { version = "1.0", features = ["derive"] } # For serialization
|
serde = { version = "1.0", features = ["derive"] } # For serialization
|
||||||
serde_yaml = "0.9"
|
serde_yaml = "0.9"
|
||||||
dirs = "4.0"
|
etcetera = "0.8.0"
|
||||||
reqwest = { version = "0.12.9", features = [
|
reqwest = { version = "0.12.9", features = [
|
||||||
"rustls-tls",
|
"rustls-tls",
|
||||||
"json",
|
"json",
|
||||||
@@ -46,11 +46,13 @@ tracing = "0.1"
|
|||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt", "json", "time"] }
|
tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt", "json", "time"] }
|
||||||
tracing-appender = "0.2"
|
tracing-appender = "0.2"
|
||||||
|
once_cell = "1.20.2"
|
||||||
winapi = { version = "0.3", features = ["wincred"], optional = true }
|
winapi = { version = "0.3", features = ["wincred"], optional = true }
|
||||||
|
|
||||||
[target.'cfg(target_os = "windows")'.dependencies]
|
[target.'cfg(target_os = "windows")'.dependencies]
|
||||||
winapi = { version = "0.3", features = ["wincred"] }
|
winapi = { version = "0.3", features = ["wincred"] }
|
||||||
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tempfile = "3"
|
tempfile = "3"
|
||||||
temp-env = { version = "0.3.6", features = ["async_closure"] }
|
temp-env = { version = "0.3.6", features = ["async_closure"] }
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use rand::{distributions::Alphanumeric, Rng};
|
|||||||
use std::process;
|
use std::process;
|
||||||
|
|
||||||
use crate::prompt::rustyline::RustylinePrompt;
|
use crate::prompt::rustyline::RustylinePrompt;
|
||||||
use crate::session::{ensure_session_dir, get_most_recent_session, Session};
|
use crate::session::{ensure_session_dir, get_most_recent_session, legacy_session_dir, Session};
|
||||||
use console::style;
|
use console::style;
|
||||||
use goose::agents::extension::{Envs, ExtensionError};
|
use goose::agents::extension::{Envs, ExtensionError};
|
||||||
use goose::agents::AgentFactory;
|
use goose::agents::AgentFactory;
|
||||||
@@ -121,9 +121,18 @@ pub async fn build_session(
|
|||||||
if session_file.exists() {
|
if session_file.exists() {
|
||||||
let prompt = Box::new(RustylinePrompt::new());
|
let prompt = Box::new(RustylinePrompt::new());
|
||||||
return Session::new(agent, prompt, session_file);
|
return Session::new(agent, prompt, session_file);
|
||||||
} else {
|
|
||||||
eprintln!("Session '{}' not found, starting new session", session_name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LEGACY NOTE: remove this once old paths are no longer needed.
|
||||||
|
if let Some(legacy_dir) = legacy_session_dir() {
|
||||||
|
let legacy_file = legacy_dir.join(format!("{}.jsonl", session_name));
|
||||||
|
if legacy_file.exists() {
|
||||||
|
let prompt = Box::new(RustylinePrompt::new());
|
||||||
|
return Session::new(agent, prompt, legacy_file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eprintln!("Session '{}' not found, starting new session", session_name);
|
||||||
} else {
|
} else {
|
||||||
// Try to resume most recent session
|
// Try to resume most recent session
|
||||||
if let Ok(session_file) = get_most_recent_session() {
|
if let Ok(session_file) = get_most_recent_session() {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use etcetera::{choose_app_strategy, AppStrategy};
|
||||||
use goose::providers::base::ProviderUsage;
|
use goose::providers::base::ProviderUsage;
|
||||||
|
|
||||||
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||||
@@ -13,8 +14,15 @@ pub fn log_usage(session_file: String, usage: Vec<ProviderUsage>) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Ensure log directory exists
|
// Ensure log directory exists
|
||||||
if let Some(home_dir) = dirs::home_dir() {
|
if let Ok(home_dir) = choose_app_strategy(crate::APP_STRATEGY.clone()) {
|
||||||
let log_dir = home_dir.join(".config").join("goose").join("logs");
|
// choose_app_strategy().state_dir()
|
||||||
|
// - macOS/Linux: ~/.local/state/goose/logs/
|
||||||
|
// - Windows: ~\AppData\Roaming\Block\goose\data\logs
|
||||||
|
// - Windows has no convention for state_dir, use data_dir instead
|
||||||
|
let log_dir = home_dir
|
||||||
|
.in_state_dir("logs")
|
||||||
|
.unwrap_or_else(|| home_dir.in_data_dir("logs"));
|
||||||
|
|
||||||
if let Err(e) = std::fs::create_dir_all(&log_dir) {
|
if let Err(e) = std::fs::create_dir_all(&log_dir) {
|
||||||
eprintln!("Failed to create log directory: {}", e);
|
eprintln!("Failed to create log directory: {}", e);
|
||||||
return;
|
return;
|
||||||
@@ -49,6 +57,7 @@ pub fn log_usage(session_file: String, usage: Vec<ProviderUsage>) {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use etcetera::{choose_app_strategy, AppStrategy};
|
||||||
use goose::providers::base::{ProviderUsage, Usage};
|
use goose::providers::base::{ProviderUsage, Usage};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -59,11 +68,11 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_session_logging() {
|
fn test_session_logging() {
|
||||||
run_with_tmp_dir(|| {
|
run_with_tmp_dir(|| {
|
||||||
let home_dir = dirs::home_dir().unwrap();
|
let home_dir = choose_app_strategy(crate::APP_STRATEGY.clone()).unwrap();
|
||||||
|
|
||||||
let log_file = home_dir
|
let log_file = home_dir
|
||||||
.join(".config")
|
.in_state_dir("logs")
|
||||||
.join("goose")
|
.unwrap_or_else(|| home_dir.in_data_dir("logs"))
|
||||||
.join("logs")
|
|
||||||
.join("goose.log");
|
.join("goose.log");
|
||||||
|
|
||||||
log_usage(
|
log_usage(
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
|
use etcetera::{choose_app_strategy, AppStrategy};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use tracing_appender::rolling::Rotation;
|
use tracing_appender::rolling::Rotation;
|
||||||
@@ -12,17 +13,16 @@ use goose::tracing::langfuse_layer;
|
|||||||
/// Returns the directory where log files should be stored.
|
/// Returns the directory where log files should be stored.
|
||||||
/// Creates the directory structure if it doesn't exist.
|
/// Creates the directory structure if it doesn't exist.
|
||||||
fn get_log_directory() -> Result<PathBuf> {
|
fn get_log_directory() -> Result<PathBuf> {
|
||||||
let home = if cfg!(windows) {
|
// choose_app_strategy().state_dir()
|
||||||
std::env::var("USERPROFILE").context("USERPROFILE environment variable not set")?
|
// - macOS/Linux: ~/.local/state/goose/logs/cli
|
||||||
} else {
|
// - Windows: ~\AppData\Roaming\Block\goose\data\logs\cli
|
||||||
std::env::var("HOME").context("HOME environment variable not set")?
|
// - Windows has no convention for state_dir, use data_dir instead
|
||||||
};
|
let home_dir = choose_app_strategy(crate::APP_STRATEGY.clone())
|
||||||
|
.context("HOME environment variable not set")?;
|
||||||
|
|
||||||
let base_log_dir = PathBuf::from(home)
|
let base_log_dir = home_dir
|
||||||
.join(".config")
|
.in_state_dir("logs/cli")
|
||||||
.join("goose")
|
.unwrap_or_else(|| home_dir.in_data_dir("logs/cli"));
|
||||||
.join("logs")
|
|
||||||
.join("cli"); // Add cli-specific subdirectory
|
|
||||||
|
|
||||||
// Create date-based subdirectory
|
// Create date-based subdirectory
|
||||||
let now = chrono::Local::now();
|
let now = chrono::Local::now();
|
||||||
|
|||||||
@@ -1,5 +1,13 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use clap::{CommandFactory, Parser, Subcommand};
|
use clap::{CommandFactory, Parser, Subcommand};
|
||||||
|
use etcetera::AppStrategyArgs;
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
|
pub static APP_STRATEGY: Lazy<AppStrategyArgs> = Lazy::new(|| AppStrategyArgs {
|
||||||
|
top_level_domain: "Block".to_string(),
|
||||||
|
author: "Block".to_string(),
|
||||||
|
app_name: "goose".to_string(),
|
||||||
|
});
|
||||||
|
|
||||||
mod commands;
|
mod commands;
|
||||||
mod log_usage;
|
mod log_usage;
|
||||||
|
|||||||
@@ -30,8 +30,8 @@ fn shorten_path(path: &str) -> String {
|
|||||||
let path = PathBuf::from(path);
|
let path = PathBuf::from(path);
|
||||||
|
|
||||||
// First try to convert to ~ if it's in home directory
|
// First try to convert to ~ if it's in home directory
|
||||||
let home = dirs::home_dir();
|
let home = etcetera::home_dir();
|
||||||
let path_str = if let Some(home) = home {
|
let path_str = if let Ok(home) = home {
|
||||||
if let Ok(stripped) = path.strip_prefix(home) {
|
if let Ok(stripped) = path.strip_prefix(home) {
|
||||||
format!("~/{}", stripped.display())
|
format!("~/{}", stripped.display())
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use core::panic;
|
use core::panic;
|
||||||
|
use etcetera::{choose_app_strategy, AppStrategy};
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use std::fs::{self, File};
|
use std::fs::{self, File};
|
||||||
use std::io::{self, BufRead, Write};
|
use std::io::{self, BufRead, Write};
|
||||||
@@ -14,8 +15,12 @@ use mcp_core::role::Role;
|
|||||||
|
|
||||||
// File management functions
|
// File management functions
|
||||||
pub fn ensure_session_dir() -> Result<PathBuf> {
|
pub fn ensure_session_dir() -> Result<PathBuf> {
|
||||||
let home_dir = dirs::home_dir().ok_or(anyhow::anyhow!("Could not determine home directory"))?;
|
// choose_app_strategy().data_dir()
|
||||||
let config_dir = home_dir.join(".config").join("goose").join("sessions");
|
// - macOS/Linux: ~/.local/share/goose/sessions/
|
||||||
|
// - Windows: ~\AppData\Roaming\Block\goose\data\sessions
|
||||||
|
let config_dir = choose_app_strategy(crate::APP_STRATEGY.clone())
|
||||||
|
.expect("goose requires a home dir")
|
||||||
|
.in_data_dir("sessions");
|
||||||
|
|
||||||
if !config_dir.exists() {
|
if !config_dir.exists() {
|
||||||
fs::create_dir_all(&config_dir)?;
|
fs::create_dir_all(&config_dir)?;
|
||||||
@@ -24,6 +29,15 @@ pub fn ensure_session_dir() -> Result<PathBuf> {
|
|||||||
Ok(config_dir)
|
Ok(config_dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// LEGACY NOTE: remove this once old paths are no longer needed.
|
||||||
|
pub fn legacy_session_dir() -> Option<PathBuf> {
|
||||||
|
// legacy path was in the config dir ~/.config/goose/sessions/
|
||||||
|
// ignore errors if we can't re-create the legacy session dir
|
||||||
|
choose_app_strategy(crate::APP_STRATEGY.clone())
|
||||||
|
.map(|strategy| strategy.in_config_dir("sessions"))
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_most_recent_session() -> Result<PathBuf> {
|
pub fn get_most_recent_session() -> Result<PathBuf> {
|
||||||
let session_dir = ensure_session_dir()?;
|
let session_dir = ensure_session_dir()?;
|
||||||
let mut entries = fs::read_dir(&session_dir)?
|
let mut entries = fs::read_dir(&session_dir)?
|
||||||
@@ -31,6 +45,19 @@ pub fn get_most_recent_session() -> Result<PathBuf> {
|
|||||||
.filter(|entry| entry.path().extension().is_some_and(|ext| ext == "jsonl"))
|
.filter(|entry| entry.path().extension().is_some_and(|ext| ext == "jsonl"))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
// LEGACY NOTE: remove this once old paths are no longer needed.
|
||||||
|
if entries.is_empty() {
|
||||||
|
if let Some(old_dir) = legacy_session_dir() {
|
||||||
|
// okay to return the error via ?, since that means we have no sessions in the
|
||||||
|
// new location, and this old location doesn't exist, so a new session will be created
|
||||||
|
let old_entries = fs::read_dir(&old_dir)?
|
||||||
|
.filter_map(|entry| entry.ok())
|
||||||
|
.filter(|entry| entry.path().extension().is_some_and(|ext| ext == "jsonl"))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
entries.extend(old_entries);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if entries.is_empty() {
|
if entries.is_empty() {
|
||||||
return Err(anyhow::anyhow!("No session files found"));
|
return Err(anyhow::anyhow!("No session files found"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,13 +29,14 @@ xcap = "0.0.14"
|
|||||||
reqwest = { version = "0.11", features = ["json", "rustls-tls"] , default-features = false}
|
reqwest = { version = "0.11", features = ["json", "rustls-tls"] , default-features = false}
|
||||||
async-trait = "0.1"
|
async-trait = "0.1"
|
||||||
chrono = { version = "0.4.38", features = ["serde"] }
|
chrono = { version = "0.4.38", features = ["serde"] }
|
||||||
dirs = "5.0.1"
|
etcetera = "0.8.0"
|
||||||
tempfile = "3.8"
|
tempfile = "3.8"
|
||||||
include_dir = "0.7.4"
|
include_dir = "0.7.4"
|
||||||
google-drive3 = "6.0.0"
|
google-drive3 = "6.0.0"
|
||||||
webbrowser = "0.8"
|
webbrowser = "0.8"
|
||||||
http-body-util = "0.1.2"
|
http-body-util = "0.1.2"
|
||||||
regex = "1.11.1"
|
regex = "1.11.1"
|
||||||
|
once_cell = "1.20.2"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
serial_test = "3.0.0"
|
serial_test = "3.0.0"
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use base64::Engine;
|
use base64::Engine;
|
||||||
|
use etcetera::{choose_app_strategy, AppStrategy};
|
||||||
use indoc::{formatdoc, indoc};
|
use indoc::{formatdoc, indoc};
|
||||||
use reqwest::{Client, Url};
|
use reqwest::{Client, Url};
|
||||||
use serde_json::{json, Value};
|
use serde_json::{json, Value};
|
||||||
@@ -216,11 +217,13 @@ impl ComputerControllerRouter {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Create cache directory in user's home directory
|
// choose_app_strategy().cache_dir()
|
||||||
let cache_dir = dirs::cache_dir()
|
// - macOS/Linux: ~/.cache/goose/computer_controller/
|
||||||
.unwrap_or_else(|| create_system_automation().get_temp_path())
|
// - Windows: ~\AppData\Local\Block\goose\cache\computer_controller\
|
||||||
.join("goose")
|
// keep previous behavior of defaulting to /tmp/
|
||||||
.join("computer_controller");
|
let cache_dir = choose_app_strategy(crate::APP_STRATEGY.clone())
|
||||||
|
.map(|strategy| strategy.in_cache_dir("computer_controller"))
|
||||||
|
.unwrap_or_else(|_| create_system_automation().get_temp_path());
|
||||||
|
|
||||||
fs::create_dir_all(&cache_dir).unwrap_or_else(|_| {
|
fs::create_dir_all(&cache_dir).unwrap_or_else(|_| {
|
||||||
println!(
|
println!(
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ mod shell;
|
|||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use base64::Engine;
|
use base64::Engine;
|
||||||
|
use etcetera::{choose_app_strategy, AppStrategy};
|
||||||
use indoc::formatdoc;
|
use indoc::formatdoc;
|
||||||
use serde_json::{json, Value};
|
use serde_json::{json, Value};
|
||||||
use std::{
|
use std::{
|
||||||
@@ -278,9 +279,16 @@ impl DeveloperRouter {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check for global hints in ~/.config/goose/.goosehints
|
// choose_app_strategy().config_dir()
|
||||||
let global_hints_path =
|
// - macOS/Linux: ~/.config/goose/
|
||||||
PathBuf::from(shellexpand::tilde("~/.config/goose/.goosehints").to_string());
|
// - Windows: ~\AppData\Roaming\Block\goose\config\
|
||||||
|
// keep previous behavior of expanding ~/.config in case this fails
|
||||||
|
let global_hints_path = choose_app_strategy(crate::APP_STRATEGY.clone())
|
||||||
|
.map(|strategy| strategy.in_config_dir(".goosehints"))
|
||||||
|
.unwrap_or_else(|_| {
|
||||||
|
PathBuf::from(shellexpand::tilde("~/.config/goose/.goosehints").to_string())
|
||||||
|
});
|
||||||
|
|
||||||
// Create the directory if it doesn't exist
|
// Create the directory if it doesn't exist
|
||||||
let _ = std::fs::create_dir_all(global_hints_path.parent().unwrap());
|
let _ = std::fs::create_dir_all(global_hints_path.parent().unwrap());
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,12 @@
|
|||||||
|
use etcetera::AppStrategyArgs;
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
|
pub static APP_STRATEGY: Lazy<AppStrategyArgs> = Lazy::new(|| AppStrategyArgs {
|
||||||
|
top_level_domain: "Block".to_string(),
|
||||||
|
author: "Block".to_string(),
|
||||||
|
app_name: "goose".to_string(),
|
||||||
|
});
|
||||||
|
|
||||||
mod computercontroller;
|
mod computercontroller;
|
||||||
mod developer;
|
mod developer;
|
||||||
mod google_drive;
|
mod google_drive;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
use etcetera::{choose_app_strategy, AppStrategy};
|
||||||
use indoc::formatdoc;
|
use indoc::formatdoc;
|
||||||
use serde_json::{json, Value};
|
use serde_json::{json, Value};
|
||||||
use std::{
|
use std::{
|
||||||
@@ -178,10 +179,13 @@ impl MemoryRouter {
|
|||||||
.join(".goose")
|
.join(".goose")
|
||||||
.join("memory");
|
.join("memory");
|
||||||
|
|
||||||
// Check for .config/goose/memory in user's home directory
|
// choose_app_strategy().config_dir()
|
||||||
let global_memory_dir = dirs::home_dir()
|
// - macOS/Linux: ~/.config/goose/memory/
|
||||||
.map(|home| home.join(".config/goose/memory"))
|
// - Windows: ~\AppData\Roaming\Block\goose\config\memory
|
||||||
.unwrap_or_else(|| PathBuf::from(".config/goose/memory"));
|
// if it fails, fall back to `.config/goose/memory` (relative to the current dir)
|
||||||
|
let global_memory_dir = choose_app_strategy(crate::APP_STRATEGY.clone())
|
||||||
|
.map(|strategy| strategy.in_config_dir("memory"))
|
||||||
|
.unwrap_or_else(|_| PathBuf::from(".config/goose/memory"));
|
||||||
|
|
||||||
fs::create_dir_all(&global_memory_dir).unwrap();
|
fs::create_dir_all(&global_memory_dir).unwrap();
|
||||||
fs::create_dir_all(&local_memory_dir).unwrap();
|
fs::create_dir_all(&local_memory_dir).unwrap();
|
||||||
|
|||||||
@@ -29,7 +29,8 @@ http = "1.0"
|
|||||||
config = { version = "0.14.1", features = ["toml"] }
|
config = { version = "0.14.1", features = ["toml"] }
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
clap = { version = "4.4", features = ["derive"] }
|
clap = { version = "4.4", features = ["derive"] }
|
||||||
once_cell = "1.18"
|
once_cell = "1.20.2"
|
||||||
|
etcetera = "0.8.0"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "goosed"
|
name = "goosed"
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
|
use etcetera::{choose_app_strategy, AppStrategy};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use tracing_appender::rolling::Rotation;
|
use tracing_appender::rolling::Rotation;
|
||||||
@@ -12,17 +13,16 @@ use goose::tracing::langfuse_layer;
|
|||||||
/// Returns the directory where log files should be stored.
|
/// Returns the directory where log files should be stored.
|
||||||
/// Creates the directory structure if it doesn't exist.
|
/// Creates the directory structure if it doesn't exist.
|
||||||
fn get_log_directory() -> Result<PathBuf> {
|
fn get_log_directory() -> Result<PathBuf> {
|
||||||
let home = if cfg!(windows) {
|
// choose_app_strategy().state_dir()
|
||||||
std::env::var("USERPROFILE").context("USERPROFILE environment variable not set")?
|
// - macOS/Linux: ~/.local/state/goose/logs/server
|
||||||
} else {
|
// - Windows: ~\AppData\Roaming\Block\goose\data\logs\server
|
||||||
std::env::var("HOME").context("HOME environment variable not set")?
|
// - Windows has no convention for state_dir, use data_dir instead
|
||||||
};
|
let home_dir = choose_app_strategy(crate::APP_STRATEGY.clone())
|
||||||
|
.context("HOME environment variable not set")?;
|
||||||
|
|
||||||
let base_log_dir = PathBuf::from(home)
|
let base_log_dir = home_dir
|
||||||
.join(".config")
|
.in_state_dir("logs/server")
|
||||||
.join("goose")
|
.unwrap_or_else(|| home_dir.in_data_dir("logs/server"));
|
||||||
.join("logs")
|
|
||||||
.join("server"); // Add server-specific subdirectory
|
|
||||||
|
|
||||||
// Create date-based subdirectory
|
// Create date-based subdirectory
|
||||||
let now = chrono::Local::now();
|
let now = chrono::Local::now();
|
||||||
|
|||||||
@@ -1,3 +1,12 @@
|
|||||||
|
use etcetera::AppStrategyArgs;
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
|
pub static APP_STRATEGY: Lazy<AppStrategyArgs> = Lazy::new(|| AppStrategyArgs {
|
||||||
|
top_level_domain: "Block".to_string(),
|
||||||
|
author: "Block".to_string(),
|
||||||
|
app_name: "goose".to_string(),
|
||||||
|
});
|
||||||
|
|
||||||
mod commands;
|
mod commands;
|
||||||
mod configuration;
|
mod configuration;
|
||||||
mod error;
|
mod error;
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ ctor = "0.2.7"
|
|||||||
paste = "1.0"
|
paste = "1.0"
|
||||||
serde_yaml = "0.9.34"
|
serde_yaml = "0.9.34"
|
||||||
once_cell = "1.20.2"
|
once_cell = "1.20.2"
|
||||||
dirs = "6.0.0"
|
etcetera = "0.8.0"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
|
|
||||||
# For Bedrock provider
|
# For Bedrock provider
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
|
use etcetera::{choose_app_strategy, AppStrategy, AppStrategyArgs};
|
||||||
use keyring::Entry;
|
use keyring::Entry;
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::{Lazy, OnceCell};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@@ -7,6 +8,12 @@ use std::env;
|
|||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
pub static APP_STRATEGY: Lazy<AppStrategyArgs> = Lazy::new(|| AppStrategyArgs {
|
||||||
|
top_level_domain: "Block".to_string(),
|
||||||
|
author: "Block".to_string(),
|
||||||
|
app_name: "goose".to_string(),
|
||||||
|
});
|
||||||
|
|
||||||
const KEYRING_SERVICE: &str = "goose";
|
const KEYRING_SERVICE: &str = "goose";
|
||||||
const KEYRING_USERNAME: &str = "secrets";
|
const KEYRING_USERNAME: &str = "secrets";
|
||||||
|
|
||||||
@@ -99,10 +106,13 @@ static GLOBAL_CONFIG: OnceCell<Config> = OnceCell::new();
|
|||||||
|
|
||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let config_dir = dirs::home_dir()
|
// choose_app_strategy().config_dir()
|
||||||
|
// - macOS/Linux: ~/.config/goose/
|
||||||
|
// - Windows: ~\AppData\Roaming\Block\goose\config\
|
||||||
|
let config_dir = choose_app_strategy(APP_STRATEGY.clone())
|
||||||
.expect("goose requires a home dir")
|
.expect("goose requires a home dir")
|
||||||
.join(".config")
|
.config_dir();
|
||||||
.join("goose");
|
|
||||||
std::fs::create_dir_all(&config_dir).expect("Failed to create config directory");
|
std::fs::create_dir_all(&config_dir).expect("Failed to create config directory");
|
||||||
|
|
||||||
let config_path = config_dir.join("config.yaml");
|
let config_path = config_dir.join("config.yaml");
|
||||||
|
|||||||
@@ -2,5 +2,5 @@ mod base;
|
|||||||
mod extensions;
|
mod extensions;
|
||||||
|
|
||||||
pub use crate::agents::ExtensionConfig;
|
pub use crate::agents::ExtensionConfig;
|
||||||
pub use base::{Config, ConfigError};
|
pub use base::{Config, ConfigError, APP_STRATEGY};
|
||||||
pub use extensions::{ExtensionEntry, ExtensionManager};
|
pub use extensions::{ExtensionEntry, ExtensionManager};
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ use anyhow::Result;
|
|||||||
use axum::{extract::Query, response::Html, routing::get, Router};
|
use axum::{extract::Query, response::Html, routing::get, Router};
|
||||||
use base64::Engine;
|
use base64::Engine;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
|
use etcetera::{choose_app_strategy, AppStrategy};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
@@ -31,13 +32,12 @@ struct TokenCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_base_path() -> PathBuf {
|
fn get_base_path() -> PathBuf {
|
||||||
const BASE_PATH: &str = ".config/goose/databricks/oauth";
|
// choose_app_strategy().config_dir()
|
||||||
let home_dir = if cfg!(windows) {
|
// - macOS/Linux: ~/.config/goose/databricks/oauth
|
||||||
std::env::var("USERPROFILE").expect("USERPROFILE environment variable not set")
|
// - Windows: ~\AppData\Roaming\Block\goose\config\databricks\oauth\
|
||||||
} else {
|
choose_app_strategy(crate::config::APP_STRATEGY.clone())
|
||||||
std::env::var("HOME").expect("HOME environment variable not set")
|
.expect("goose requires a home dir")
|
||||||
};
|
.in_config_dir("databricks/oauth")
|
||||||
PathBuf::from(home_dir).join(BASE_PATH)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TokenCache {
|
impl TokenCache {
|
||||||
|
|||||||
Reference in New Issue
Block a user