Make it startable from playwright and also isolate (#5016)
Co-authored-by: Douwe Osinga <douwe@squareup.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
use etcetera::{choose_app_strategy, AppStrategy, AppStrategyArgs};
|
||||
use crate::config::paths::Paths;
|
||||
use fs2::FileExt;
|
||||
use keyring::Entry;
|
||||
use once_cell::sync::{Lazy, OnceCell};
|
||||
use once_cell::sync::OnceCell;
|
||||
use serde::Deserialize;
|
||||
use serde_json::Value;
|
||||
use std::collections::HashMap;
|
||||
@@ -11,12 +11,6 @@ use std::io::Write;
|
||||
use std::path::{Path, PathBuf};
|
||||
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_USERNAME: &str = "secrets";
|
||||
|
||||
@@ -116,18 +110,9 @@ enum SecretStorage {
|
||||
// Global instance
|
||||
static GLOBAL_CONFIG: OnceCell<Config> = OnceCell::new();
|
||||
|
||||
pub fn get_config_dir() -> PathBuf {
|
||||
choose_app_strategy(APP_STRATEGY.clone())
|
||||
.expect("goose requires a home dir")
|
||||
.config_dir()
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
// choose_app_strategy().config_dir()
|
||||
// - macOS/Linux: ~/.config/goose/
|
||||
// - Windows: ~\AppData\Roaming\Block\goose\config\
|
||||
let config_dir = get_config_dir();
|
||||
let config_dir = Paths::config_dir();
|
||||
|
||||
std::fs::create_dir_all(&config_dir).expect("Failed to create config directory");
|
||||
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
use crate::config::{Config, APP_STRATEGY};
|
||||
use crate::config::paths::Paths;
|
||||
use crate::config::Config;
|
||||
use crate::model::ModelConfig;
|
||||
use crate::providers::anthropic::AnthropicProvider;
|
||||
use crate::providers::base::ModelInfo;
|
||||
use crate::providers::ollama::OllamaProvider;
|
||||
use crate::providers::openai::OpenAiProvider;
|
||||
use anyhow::Result;
|
||||
use etcetera::{choose_app_strategy, AppStrategy};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
|
||||
pub fn custom_providers_dir() -> std::path::PathBuf {
|
||||
choose_app_strategy(APP_STRATEGY.clone())
|
||||
.expect("goose requires a home dir")
|
||||
.config_dir()
|
||||
.join("custom_providers")
|
||||
Paths::config_dir().join("custom_providers")
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
|
||||
@@ -2,12 +2,13 @@ pub mod base;
|
||||
pub mod custom_providers;
|
||||
mod experiments;
|
||||
pub mod extensions;
|
||||
pub mod paths;
|
||||
pub mod permission;
|
||||
pub mod signup_openrouter;
|
||||
pub mod signup_tetrate;
|
||||
|
||||
pub use crate::agents::ExtensionConfig;
|
||||
pub use base::{get_config_dir, Config, ConfigError, APP_STRATEGY};
|
||||
pub use base::{Config, ConfigError};
|
||||
pub use custom_providers::CustomProviderConfig;
|
||||
pub use experiments::ExperimentManager;
|
||||
pub use extensions::{
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
use etcetera::{choose_app_strategy, AppStrategy, AppStrategyArgs};
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub struct Paths;
|
||||
|
||||
impl Paths {
|
||||
fn get_dir(dir_type: DirType) -> PathBuf {
|
||||
if let Ok(test_root) = std::env::var("GOOSE_PATH_ROOT") {
|
||||
let base = PathBuf::from(test_root);
|
||||
match dir_type {
|
||||
DirType::Config => base.join("config"),
|
||||
DirType::Data => base.join("data"),
|
||||
DirType::State => base.join("state"),
|
||||
}
|
||||
} else {
|
||||
let strategy = choose_app_strategy(AppStrategyArgs {
|
||||
top_level_domain: "Block".to_string(),
|
||||
author: "Block".to_string(),
|
||||
app_name: "goose".to_string(),
|
||||
})
|
||||
.expect("goose requires a home dir");
|
||||
|
||||
match dir_type {
|
||||
DirType::Config => strategy.config_dir(),
|
||||
DirType::Data => strategy.data_dir(),
|
||||
DirType::State => strategy.state_dir().unwrap_or(strategy.data_dir()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn config_dir() -> PathBuf {
|
||||
Self::get_dir(DirType::Config)
|
||||
}
|
||||
|
||||
pub fn data_dir() -> PathBuf {
|
||||
Self::get_dir(DirType::Data)
|
||||
}
|
||||
|
||||
pub fn state_dir() -> PathBuf {
|
||||
Self::get_dir(DirType::State)
|
||||
}
|
||||
|
||||
pub fn in_state_dir(subpath: &str) -> PathBuf {
|
||||
Self::state_dir().join(subpath)
|
||||
}
|
||||
|
||||
pub fn in_config_dir(subpath: &str) -> PathBuf {
|
||||
Self::config_dir().join(subpath)
|
||||
}
|
||||
|
||||
pub fn in_data_dir(subpath: &str) -> PathBuf {
|
||||
Self::data_dir().join(subpath)
|
||||
}
|
||||
}
|
||||
|
||||
enum DirType {
|
||||
Config,
|
||||
Data,
|
||||
State,
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
use super::APP_STRATEGY;
|
||||
use etcetera::{choose_app_strategy, AppStrategy};
|
||||
use crate::config::paths::Paths;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
@@ -37,14 +36,7 @@ const SMART_APPROVE_PERMISSION: &str = "smart_approve";
|
||||
/// Implements the default constructor for `PermissionManager`.
|
||||
impl Default for PermissionManager {
|
||||
fn default() -> Self {
|
||||
// Choose the app strategy and determine the config directory
|
||||
let config_dir = choose_app_strategy(APP_STRATEGY.clone())
|
||||
.expect("goose requires a home dir")
|
||||
.config_dir();
|
||||
|
||||
// Ensure the configuration directory exists
|
||||
std::fs::create_dir_all(&config_dir).expect("Failed to create config directory");
|
||||
let config_path = config_dir.join("permission.yaml");
|
||||
let config_path = Paths::config_dir().join("permission.yaml");
|
||||
|
||||
// Load the existing configuration file or create an empty map if the file doesn't exist
|
||||
let permission_map = if config_path.exists() {
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
use crate::agents::extension::PlatformExtensionContext;
|
||||
use crate::agents::Agent;
|
||||
use crate::config::APP_STRATEGY;
|
||||
use crate::config::paths::Paths;
|
||||
use crate::model::ModelConfig;
|
||||
use crate::providers::create;
|
||||
use crate::scheduler_factory::SchedulerFactory;
|
||||
use crate::scheduler_trait::SchedulerTrait;
|
||||
use anyhow::Result;
|
||||
use etcetera::{choose_app_strategy, AppStrategy};
|
||||
use lru::LruCache;
|
||||
use std::num::NonZeroUsize;
|
||||
use std::sync::Arc;
|
||||
@@ -37,10 +36,7 @@ impl AgentManager {
|
||||
|
||||
// Private constructor - prevents direct instantiation in production
|
||||
async fn new(max_sessions: Option<usize>) -> Result<Self> {
|
||||
// Construct scheduler with the standard goose-server path
|
||||
let schedule_file_path = choose_app_strategy(APP_STRATEGY.clone())?
|
||||
.data_dir()
|
||||
.join("schedule.json");
|
||||
let schedule_file_path = Paths::data_dir().join("schedule.json");
|
||||
|
||||
let scheduler = SchedulerFactory::create(schedule_file_path).await?;
|
||||
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
use crate::config::paths::Paths;
|
||||
use anyhow::{Context, Result};
|
||||
use etcetera::{choose_app_strategy, AppStrategy};
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::config::APP_STRATEGY;
|
||||
|
||||
/// Returns the directory where log files should be stored for a specific component.
|
||||
/// Creates the directory structure if it doesn't exist.
|
||||
///
|
||||
@@ -12,17 +10,8 @@ use crate::config::APP_STRATEGY;
|
||||
///
|
||||
/// * `component` - The component name (e.g., "cli", "server", "debug")
|
||||
/// * `use_date_subdir` - Whether to create a date-based subdirectory
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// The path to the log directory for the specified component
|
||||
pub fn get_log_directory(component: &str, use_date_subdir: bool) -> Result<PathBuf> {
|
||||
let home_dir =
|
||||
choose_app_strategy(APP_STRATEGY.clone()).context("HOME environment variable not set")?;
|
||||
|
||||
let base_log_dir = home_dir
|
||||
.in_state_dir("logs")
|
||||
.unwrap_or_else(|| home_dir.in_data_dir("logs"));
|
||||
let base_log_dir = Paths::in_state_dir("logs");
|
||||
|
||||
let component_dir = base_log_dir.join(component);
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use crate::config::paths::Paths;
|
||||
use crate::conversation::message::ToolRequest;
|
||||
use anyhow::Result;
|
||||
use blake3::Hasher;
|
||||
use chrono::Utc;
|
||||
use etcetera::{choose_app_strategy, AppStrategy};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::time::Duration;
|
||||
@@ -35,14 +35,10 @@ impl Default for ToolPermissionStore {
|
||||
|
||||
impl ToolPermissionStore {
|
||||
pub fn new() -> Self {
|
||||
let permissions_dir = choose_app_strategy(crate::config::APP_STRATEGY.clone())
|
||||
.map(|strategy| strategy.config_dir())
|
||||
.unwrap_or_else(|_| PathBuf::from(".config/goose"));
|
||||
|
||||
Self {
|
||||
permissions: HashMap::new(),
|
||||
version: 1,
|
||||
permissions_dir,
|
||||
permissions_dir: Paths::config_dir().join("permissions"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use crate::config::paths::Paths;
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use async_trait::async_trait;
|
||||
use axum::http;
|
||||
use chrono::{DateTime, Utc};
|
||||
use etcetera::{choose_app_strategy, AppStrategy};
|
||||
use reqwest::Client;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
@@ -81,9 +81,7 @@ struct DiskCache {
|
||||
|
||||
impl DiskCache {
|
||||
fn new() -> Self {
|
||||
let cache_path = choose_app_strategy(crate::config::APP_STRATEGY.clone())
|
||||
.expect("goose requires a home dir")
|
||||
.in_config_dir("githubcopilot/info.json");
|
||||
let cache_path = Paths::in_config_dir("githubcopilot/info.json");
|
||||
Self { cache_path }
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use crate::config::paths::Paths;
|
||||
use anyhow::Result;
|
||||
use axum::{extract::Query, response::Html, routing::get, Router};
|
||||
use base64::Engine;
|
||||
use chrono::{DateTime, Utc};
|
||||
use etcetera::{choose_app_strategy, AppStrategy};
|
||||
use once_cell::sync::Lazy;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
@@ -38,12 +38,7 @@ struct TokenCache {
|
||||
}
|
||||
|
||||
fn get_base_path() -> PathBuf {
|
||||
// choose_app_strategy().config_dir()
|
||||
// - macOS/Linux: ~/.config/goose/databricks/oauth
|
||||
// - Windows: ~\AppData\Roaming\Block\goose\config\databricks\oauth\
|
||||
choose_app_strategy(crate::config::APP_STRATEGY.clone())
|
||||
.expect("goose requires a home dir")
|
||||
.in_config_dir("databricks/oauth")
|
||||
Paths::in_config_dir("databricks/oauth")
|
||||
}
|
||||
|
||||
impl TokenCache {
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use etcetera::{choose_app_strategy, AppStrategy};
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use crate::config::APP_STRATEGY;
|
||||
use crate::config::paths::Paths;
|
||||
use crate::recipe::read_recipe_file_content::{read_recipe_file, RecipeFile};
|
||||
use crate::recipe::Recipe;
|
||||
use crate::recipe::RECIPE_FILE_EXTENSIONS;
|
||||
@@ -14,10 +13,7 @@ const GOOSE_RECIPE_PATH_ENV_VAR: &str = "GOOSE_RECIPE_PATH";
|
||||
|
||||
pub fn get_recipe_library_dir(is_global: bool) -> PathBuf {
|
||||
if is_global {
|
||||
choose_app_strategy(APP_STRATEGY.clone())
|
||||
.expect("goose requires a home dir")
|
||||
.config_dir()
|
||||
.join("recipes")
|
||||
Paths::config_dir().join("recipes")
|
||||
} else {
|
||||
std::env::current_dir().unwrap().join(".goose/recipes")
|
||||
}
|
||||
|
||||
@@ -7,14 +7,14 @@ use std::sync::Arc;
|
||||
use anyhow::{anyhow, Result};
|
||||
use async_trait::async_trait;
|
||||
use chrono::{DateTime, Utc};
|
||||
use etcetera::{choose_app_strategy, AppStrategy};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::sync::Mutex;
|
||||
use tokio_cron_scheduler::{job::JobId, Job, JobScheduler as TokioJobScheduler};
|
||||
|
||||
use crate::agents::AgentEvent;
|
||||
use crate::agents::{Agent, SessionConfig};
|
||||
use crate::config::{self, Config};
|
||||
use crate::config::paths::Paths;
|
||||
use crate::config::Config;
|
||||
use crate::conversation::message::Message;
|
||||
use crate::conversation::Conversation;
|
||||
use crate::providers::base::Provider as GooseProvider; // Alias to avoid conflict in test section
|
||||
@@ -63,18 +63,13 @@ pub fn normalize_cron_expression(src: &str) -> String {
|
||||
}
|
||||
|
||||
pub fn get_default_scheduler_storage_path() -> Result<PathBuf, io::Error> {
|
||||
let strategy = choose_app_strategy(config::APP_STRATEGY.clone())
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::NotFound, e.to_string()))?;
|
||||
let data_dir = strategy.data_dir();
|
||||
let data_dir = Paths::data_dir();
|
||||
fs::create_dir_all(&data_dir)?;
|
||||
Ok(data_dir.join("schedules.json"))
|
||||
}
|
||||
|
||||
pub fn get_default_scheduled_recipes_dir() -> Result<PathBuf, SchedulerError> {
|
||||
let strategy = choose_app_strategy(config::APP_STRATEGY.clone()).map_err(|e| {
|
||||
SchedulerError::StorageError(io::Error::new(io::ErrorKind::NotFound, e.to_string()))
|
||||
})?;
|
||||
let data_dir = strategy.data_dir();
|
||||
let data_dir = Paths::data_dir();
|
||||
let recipes_dir = data_dir.join("scheduled_recipes");
|
||||
fs::create_dir_all(&recipes_dir).map_err(SchedulerError::StorageError)?;
|
||||
tracing::debug!(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::config::APP_STRATEGY;
|
||||
use crate::config::paths::Paths;
|
||||
use crate::conversation::message::Message;
|
||||
use crate::conversation::Conversation;
|
||||
use crate::providers::base::{Provider, MSG_COUNT_FOR_SESSION_NAME_GENERATION};
|
||||
@@ -6,7 +6,6 @@ use crate::recipe::Recipe;
|
||||
use crate::session::extension_data::ExtensionData;
|
||||
use anyhow::Result;
|
||||
use chrono::{DateTime, Utc};
|
||||
use etcetera::{choose_app_strategy, AppStrategy};
|
||||
use rmcp::model::Role;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::sqlite::SqliteConnectOptions;
|
||||
@@ -241,16 +240,13 @@ pub struct SessionStorage {
|
||||
}
|
||||
|
||||
pub fn ensure_session_dir() -> Result<PathBuf> {
|
||||
let data_dir = choose_app_strategy(APP_STRATEGY.clone())
|
||||
.expect("goose requires a home dir")
|
||||
.data_dir()
|
||||
.join("sessions");
|
||||
let session_dir = Paths::data_dir().join("sessions");
|
||||
|
||||
if !data_dir.exists() {
|
||||
fs::create_dir_all(&data_dir)?;
|
||||
if !session_dir.exists() {
|
||||
fs::create_dir_all(&session_dir)?;
|
||||
}
|
||||
|
||||
Ok(data_dir)
|
||||
Ok(session_dir)
|
||||
}
|
||||
|
||||
fn role_to_string(role: &Role) -> &'static str {
|
||||
|
||||
Reference in New Issue
Block a user