Make it startable from playwright and also isolate (#5016)
Co-authored-by: Douwe Osinga <douwe@squareup.com>
This commit is contained in:
Generated
+31
@@ -2705,6 +2705,7 @@ dependencies = [
|
|||||||
"jsonschema",
|
"jsonschema",
|
||||||
"nix 0.30.1",
|
"nix 0.30.1",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
"open",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"regex",
|
"regex",
|
||||||
"rmcp",
|
"rmcp",
|
||||||
@@ -3531,6 +3532,15 @@ version = "2.11.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130"
|
checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "is-docker"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "is-terminal"
|
name = "is-terminal"
|
||||||
version = "0.4.16"
|
version = "0.4.16"
|
||||||
@@ -3542,6 +3552,16 @@ dependencies = [
|
|||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "is-wsl"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5"
|
||||||
|
dependencies = [
|
||||||
|
"is-docker",
|
||||||
|
"once_cell",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "is_terminal_polyfill"
|
name = "is_terminal_polyfill"
|
||||||
version = "1.70.1"
|
version = "1.70.1"
|
||||||
@@ -4418,6 +4438,17 @@ version = "11.1.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9"
|
checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "open"
|
||||||
|
version = "5.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2483562e62ea94312f3576a7aca397306df7990b8d89033e18766744377ef95"
|
||||||
|
dependencies = [
|
||||||
|
"is-wsl",
|
||||||
|
"libc",
|
||||||
|
"pathdiff",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "openssl"
|
name = "openssl"
|
||||||
version = "0.10.73"
|
version = "0.10.73"
|
||||||
|
|||||||
@@ -143,6 +143,15 @@ run-ui:
|
|||||||
@echo "Running UI..."
|
@echo "Running UI..."
|
||||||
cd ui/desktop && npm install && npm run start-gui
|
cd ui/desktop && npm install && npm run start-gui
|
||||||
|
|
||||||
|
run-ui-playwright:
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
just release-binary
|
||||||
|
echo "Running UI with Playwright debugging..."
|
||||||
|
RUN_DIR="$HOME/goose-runs/$(date +%Y%m%d-%H%M%S)"
|
||||||
|
mkdir -p "$RUN_DIR"
|
||||||
|
echo "Using isolated directory: $RUN_DIR"
|
||||||
|
cd ui/desktop && ENABLE_PLAYWRIGHT=true GOOSE_PATH_ROOT="$RUN_DIR" npm run start-gui
|
||||||
|
|
||||||
run-ui-only:
|
run-ui-only:
|
||||||
@echo "Running UI..."
|
@echo "Running UI..."
|
||||||
cd ui/desktop && npm install && npm run start-gui
|
cd ui/desktop && npm install && npm run start-gui
|
||||||
@@ -463,4 +472,4 @@ build-test-tools:
|
|||||||
|
|
||||||
record-mcp-tests: build-test-tools
|
record-mcp-tests: build-test-tools
|
||||||
GOOSE_RECORD_MCP=1 cargo test --package goose --test mcp_integration_test
|
GOOSE_RECORD_MCP=1 cargo test --package goose --test mcp_integration_test
|
||||||
git add crates/goose/tests/mcp_replays/
|
git add crates/goose/tests/mcp_replays/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use console::style;
|
use console::style;
|
||||||
use etcetera::{choose_app_strategy, AppStrategy};
|
use goose::config::paths::Paths;
|
||||||
use goose::config::Config;
|
use goose::config::Config;
|
||||||
use serde_yaml;
|
use serde_yaml;
|
||||||
|
|
||||||
@@ -9,11 +9,8 @@ fn print_aligned(label: &str, value: &str, width: usize) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_info(verbose: bool) -> Result<()> {
|
pub fn handle_info(verbose: bool) -> Result<()> {
|
||||||
let data_dir = choose_app_strategy(crate::APP_STRATEGY.clone())?;
|
let logs_dir = Paths::in_state_dir("logs");
|
||||||
let logs_dir = data_dir
|
let sessions_dir = Paths::in_data_dir("sessions");
|
||||||
.in_state_dir("logs")
|
|
||||||
.unwrap_or_else(|| data_dir.in_data_dir("logs"));
|
|
||||||
let sessions_dir = data_dir.in_data_dir("sessions");
|
|
||||||
|
|
||||||
// Get paths using a stored reference to the global config
|
// Get paths using a stored reference to the global config
|
||||||
let config = Config::global();
|
let config = Config::global();
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
use etcetera::AppStrategyArgs;
|
|
||||||
use once_cell::sync::Lazy;
|
|
||||||
pub mod cli;
|
pub mod cli;
|
||||||
pub mod commands;
|
pub mod commands;
|
||||||
pub mod logging;
|
pub mod logging;
|
||||||
@@ -11,9 +9,3 @@ pub mod signal;
|
|||||||
|
|
||||||
// Re-export commonly used types
|
// Re-export commonly used types
|
||||||
pub use session::CliSession;
|
pub use session::CliSession;
|
||||||
|
|
||||||
pub static APP_STRATEGY: Lazy<AppStrategyArgs> = Lazy::new(|| AppStrategyArgs {
|
|
||||||
top_level_domain: "Block".to_string(),
|
|
||||||
author: "Block".to_string(),
|
|
||||||
app_name: "goose".to_string(),
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use etcetera::{choose_app_strategy, AppStrategy};
|
use goose::config::paths::Paths;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
@@ -41,11 +41,7 @@ pub struct ProjectInfoDisplay {
|
|||||||
impl ProjectTracker {
|
impl ProjectTracker {
|
||||||
/// Get the path to the projects.json file
|
/// Get the path to the projects.json file
|
||||||
fn get_projects_file() -> Result<PathBuf> {
|
fn get_projects_file() -> Result<PathBuf> {
|
||||||
let projects_file = choose_app_strategy(crate::APP_STRATEGY.clone())
|
let projects_file = Paths::in_data_dir("projects.json");
|
||||||
.context("goose requires a home dir")?
|
|
||||||
.in_data_dir("projects.json");
|
|
||||||
|
|
||||||
// Ensure data directory exists
|
|
||||||
if let Some(parent) = projects_file.parent() {
|
if let Some(parent) = projects_file.parent() {
|
||||||
if !parent.exists() {
|
if !parent.exists() {
|
||||||
fs::create_dir_all(parent)?;
|
fs::create_dir_all(parent)?;
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ use goose::utils::safe_truncate;
|
|||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use completion::GooseCompleter;
|
use completion::GooseCompleter;
|
||||||
use etcetera::{choose_app_strategy, AppStrategy};
|
|
||||||
use goose::agents::extension::{Envs, ExtensionConfig};
|
use goose::agents::extension::{Envs, ExtensionConfig};
|
||||||
use goose::agents::types::RetryConfig;
|
use goose::agents::types::RetryConfig;
|
||||||
use goose::agents::{Agent, SessionConfig};
|
use goose::agents::{Agent, SessionConfig};
|
||||||
@@ -37,6 +36,7 @@ use rmcp::model::PromptMessage;
|
|||||||
use rmcp::model::ServerNotification;
|
use rmcp::model::ServerNotification;
|
||||||
use rmcp::model::{ErrorCode, ErrorData};
|
use rmcp::model::{ErrorCode, ErrorData};
|
||||||
|
|
||||||
|
use goose::config::paths::Paths;
|
||||||
use goose::conversation::message::{Message, MessageContent};
|
use goose::conversation::message::{Message, MessageContent};
|
||||||
use rand::{distributions::Alphanumeric, Rng};
|
use rand::{distributions::Alphanumeric, Rng};
|
||||||
use rustyline::EditMode;
|
use rustyline::EditMode;
|
||||||
@@ -413,29 +413,20 @@ impl CliSession {
|
|||||||
let completer = GooseCompleter::new(self.completion_cache.clone());
|
let completer = GooseCompleter::new(self.completion_cache.clone());
|
||||||
editor.set_helper(Some(completer));
|
editor.set_helper(Some(completer));
|
||||||
|
|
||||||
// Create and use a global history file in ~/.config/goose directory
|
let history_file = Paths::config_dir().join("history.txt");
|
||||||
// This allows command history to persist across different chat sessions
|
|
||||||
// instead of being tied to each individual session's messages
|
|
||||||
let strategy =
|
|
||||||
choose_app_strategy(crate::APP_STRATEGY.clone()).expect("goose requires a home dir");
|
|
||||||
let config_dir = strategy.config_dir();
|
|
||||||
let history_file = config_dir.join("history.txt");
|
|
||||||
|
|
||||||
// Ensure config directory exists
|
|
||||||
if let Some(parent) = history_file.parent() {
|
if let Some(parent) = history_file.parent() {
|
||||||
if !parent.exists() {
|
if !parent.exists() {
|
||||||
std::fs::create_dir_all(parent)?;
|
std::fs::create_dir_all(parent)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load history from the global file
|
|
||||||
if history_file.exists() {
|
if history_file.exists() {
|
||||||
if let Err(err) = editor.load_history(&history_file) {
|
if let Err(err) = editor.load_history(&history_file) {
|
||||||
eprintln!("Warning: Failed to load command history: {}", err);
|
eprintln!("Warning: Failed to load command history: {}", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to save history after commands
|
|
||||||
let save_history =
|
let save_history =
|
||||||
|editor: &mut rustyline::Editor<GooseCompleter, rustyline::history::DefaultHistory>| {
|
|editor: &mut rustyline::Editor<GooseCompleter, rustyline::history::DefaultHistory>| {
|
||||||
if let Err(err) = editor.save_history(&history_file) {
|
if let Err(err) = editor.save_history(&history_file) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use goose::config::get_config_dir;
|
|
||||||
use std::{env, ffi::OsString, process::Stdio};
|
use std::{env, ffi::OsString, process::Stdio};
|
||||||
|
|
||||||
|
use goose::config::paths::Paths;
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
#[allow(unused_imports)] // False positive: trait is used for process_group method
|
#[allow(unused_imports)] // False positive: trait is used for process_group method
|
||||||
use std::os::unix::process::CommandExt;
|
use std::os::unix::process::CommandExt;
|
||||||
@@ -30,7 +30,7 @@ impl Default for ShellConfig {
|
|||||||
|
|
||||||
// Configure environment based on shell type
|
// Configure environment based on shell type
|
||||||
let envs = if shell_name == "bash" {
|
let envs = if shell_name == "bash" {
|
||||||
let bash_env = get_config_dir().join(".bash_env").into_os_string();
|
let bash_env = Paths::config_dir().join(".bash_env").into_os_string();
|
||||||
vec![(OsString::from("BASH_ENV"), bash_env)]
|
vec![(OsString::from("BASH_ENV"), bash_env)]
|
||||||
} else {
|
} else {
|
||||||
vec![]
|
vec![]
|
||||||
|
|||||||
@@ -5,9 +5,8 @@ use axum::{
|
|||||||
routing::{delete, get, post},
|
routing::{delete, get, post},
|
||||||
Json, Router,
|
Json, Router,
|
||||||
};
|
};
|
||||||
use etcetera::{choose_app_strategy, AppStrategy};
|
use goose::config::paths::Paths;
|
||||||
use goose::config::ExtensionEntry;
|
use goose::config::ExtensionEntry;
|
||||||
use goose::config::APP_STRATEGY;
|
|
||||||
use goose::config::{Config, ConfigError};
|
use goose::config::{Config, ConfigError};
|
||||||
use goose::model::ModelConfig;
|
use goose::model::ModelConfig;
|
||||||
use goose::providers::base::ProviderMetadata;
|
use goose::providers::base::ProviderMetadata;
|
||||||
@@ -565,11 +564,7 @@ pub async fn upsert_permissions(
|
|||||||
)
|
)
|
||||||
)]
|
)]
|
||||||
pub async fn backup_config() -> Result<Json<String>, StatusCode> {
|
pub async fn backup_config() -> Result<Json<String>, StatusCode> {
|
||||||
let config_dir = choose_app_strategy(APP_STRATEGY.clone())
|
let config_path = Paths::config_dir().join("config.yaml");
|
||||||
.expect("goose requires a home dir")
|
|
||||||
.config_dir();
|
|
||||||
|
|
||||||
let config_path = config_dir.join("config.yaml");
|
|
||||||
|
|
||||||
if config_path.exists() {
|
if config_path.exists() {
|
||||||
let file_name = config_path
|
let file_name = config_path
|
||||||
@@ -630,11 +625,7 @@ pub async fn recover_config() -> Result<Json<String>, StatusCode> {
|
|||||||
)
|
)
|
||||||
)]
|
)]
|
||||||
pub async fn validate_config() -> Result<Json<String>, StatusCode> {
|
pub async fn validate_config() -> Result<Json<String>, StatusCode> {
|
||||||
let config_dir = choose_app_strategy(APP_STRATEGY.clone())
|
let config_path = Paths::config_dir().join("config.yaml");
|
||||||
.expect("goose requires a home dir")
|
|
||||||
.config_dir();
|
|
||||||
|
|
||||||
let config_path = config_dir.join("config.yaml");
|
|
||||||
|
|
||||||
if !config_path.exists() {
|
if !config_path.exists() {
|
||||||
return Ok(Json("Config file does not exist".to_string()));
|
return Ok(Json("Config file does not exist".to_string()));
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use etcetera::{choose_app_strategy, AppStrategy, AppStrategyArgs};
|
use crate::config::paths::Paths;
|
||||||
use fs2::FileExt;
|
use fs2::FileExt;
|
||||||
use keyring::Entry;
|
use keyring::Entry;
|
||||||
use once_cell::sync::{Lazy, OnceCell};
|
use once_cell::sync::OnceCell;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@@ -11,12 +11,6 @@ use std::io::Write;
|
|||||||
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";
|
||||||
|
|
||||||
@@ -116,18 +110,9 @@ enum SecretStorage {
|
|||||||
// Global instance
|
// Global instance
|
||||||
static GLOBAL_CONFIG: OnceCell<Config> = OnceCell::new();
|
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 {
|
impl Default for Config {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
// choose_app_strategy().config_dir()
|
let config_dir = Paths::config_dir();
|
||||||
// - macOS/Linux: ~/.config/goose/
|
|
||||||
// - Windows: ~\AppData\Roaming\Block\goose\config\
|
|
||||||
let config_dir = get_config_dir();
|
|
||||||
|
|
||||||
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");
|
||||||
|
|
||||||
|
|||||||
@@ -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::model::ModelConfig;
|
||||||
use crate::providers::anthropic::AnthropicProvider;
|
use crate::providers::anthropic::AnthropicProvider;
|
||||||
use crate::providers::base::ModelInfo;
|
use crate::providers::base::ModelInfo;
|
||||||
use crate::providers::ollama::OllamaProvider;
|
use crate::providers::ollama::OllamaProvider;
|
||||||
use crate::providers::openai::OpenAiProvider;
|
use crate::providers::openai::OpenAiProvider;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use etcetera::{choose_app_strategy, AppStrategy};
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
pub fn custom_providers_dir() -> std::path::PathBuf {
|
pub fn custom_providers_dir() -> std::path::PathBuf {
|
||||||
choose_app_strategy(APP_STRATEGY.clone())
|
Paths::config_dir().join("custom_providers")
|
||||||
.expect("goose requires a home dir")
|
|
||||||
.config_dir()
|
|
||||||
.join("custom_providers")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
|||||||
@@ -2,12 +2,13 @@ pub mod base;
|
|||||||
pub mod custom_providers;
|
pub mod custom_providers;
|
||||||
mod experiments;
|
mod experiments;
|
||||||
pub mod extensions;
|
pub mod extensions;
|
||||||
|
pub mod paths;
|
||||||
pub mod permission;
|
pub mod permission;
|
||||||
pub mod signup_openrouter;
|
pub mod signup_openrouter;
|
||||||
pub mod signup_tetrate;
|
pub mod signup_tetrate;
|
||||||
|
|
||||||
pub use crate::agents::ExtensionConfig;
|
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 custom_providers::CustomProviderConfig;
|
||||||
pub use experiments::ExperimentManager;
|
pub use experiments::ExperimentManager;
|
||||||
pub use extensions::{
|
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 crate::config::paths::Paths;
|
||||||
use etcetera::{choose_app_strategy, AppStrategy};
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
@@ -37,14 +36,7 @@ const SMART_APPROVE_PERMISSION: &str = "smart_approve";
|
|||||||
/// Implements the default constructor for `PermissionManager`.
|
/// Implements the default constructor for `PermissionManager`.
|
||||||
impl Default for PermissionManager {
|
impl Default for PermissionManager {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
// Choose the app strategy and determine the config directory
|
let config_path = Paths::config_dir().join("permission.yaml");
|
||||||
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");
|
|
||||||
|
|
||||||
// Load the existing configuration file or create an empty map if the file doesn't exist
|
// Load the existing configuration file or create an empty map if the file doesn't exist
|
||||||
let permission_map = if config_path.exists() {
|
let permission_map = if config_path.exists() {
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
use crate::agents::extension::PlatformExtensionContext;
|
use crate::agents::extension::PlatformExtensionContext;
|
||||||
use crate::agents::Agent;
|
use crate::agents::Agent;
|
||||||
use crate::config::APP_STRATEGY;
|
use crate::config::paths::Paths;
|
||||||
use crate::model::ModelConfig;
|
use crate::model::ModelConfig;
|
||||||
use crate::providers::create;
|
use crate::providers::create;
|
||||||
use crate::scheduler_factory::SchedulerFactory;
|
use crate::scheduler_factory::SchedulerFactory;
|
||||||
use crate::scheduler_trait::SchedulerTrait;
|
use crate::scheduler_trait::SchedulerTrait;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use etcetera::{choose_app_strategy, AppStrategy};
|
|
||||||
use lru::LruCache;
|
use lru::LruCache;
|
||||||
use std::num::NonZeroUsize;
|
use std::num::NonZeroUsize;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -37,10 +36,7 @@ impl AgentManager {
|
|||||||
|
|
||||||
// Private constructor - prevents direct instantiation in production
|
// Private constructor - prevents direct instantiation in production
|
||||||
async fn new(max_sessions: Option<usize>) -> Result<Self> {
|
async fn new(max_sessions: Option<usize>) -> Result<Self> {
|
||||||
// Construct scheduler with the standard goose-server path
|
let schedule_file_path = Paths::data_dir().join("schedule.json");
|
||||||
let schedule_file_path = choose_app_strategy(APP_STRATEGY.clone())?
|
|
||||||
.data_dir()
|
|
||||||
.join("schedule.json");
|
|
||||||
|
|
||||||
let scheduler = SchedulerFactory::create(schedule_file_path).await?;
|
let scheduler = SchedulerFactory::create(schedule_file_path).await?;
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
|
use crate::config::paths::Paths;
|
||||||
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 crate::config::APP_STRATEGY;
|
|
||||||
|
|
||||||
/// Returns the directory where log files should be stored for a specific component.
|
/// Returns the directory where log files should be stored for a specific component.
|
||||||
/// Creates the directory structure if it doesn't exist.
|
/// 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")
|
/// * `component` - The component name (e.g., "cli", "server", "debug")
|
||||||
/// * `use_date_subdir` - Whether to create a date-based subdirectory
|
/// * `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> {
|
pub fn get_log_directory(component: &str, use_date_subdir: bool) -> Result<PathBuf> {
|
||||||
let home_dir =
|
let base_log_dir = Paths::in_state_dir("logs");
|
||||||
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 component_dir = base_log_dir.join(component);
|
let component_dir = base_log_dir.join(component);
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
|
use crate::config::paths::Paths;
|
||||||
use crate::conversation::message::ToolRequest;
|
use crate::conversation::message::ToolRequest;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use blake3::Hasher;
|
use blake3::Hasher;
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use etcetera::{choose_app_strategy, AppStrategy};
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
@@ -35,14 +35,10 @@ impl Default for ToolPermissionStore {
|
|||||||
|
|
||||||
impl ToolPermissionStore {
|
impl ToolPermissionStore {
|
||||||
pub fn new() -> Self {
|
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 {
|
Self {
|
||||||
permissions: HashMap::new(),
|
permissions: HashMap::new(),
|
||||||
version: 1,
|
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 anyhow::{anyhow, Context, Result};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use axum::http;
|
use axum::http;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use etcetera::{choose_app_strategy, AppStrategy};
|
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
@@ -81,9 +81,7 @@ struct DiskCache {
|
|||||||
|
|
||||||
impl DiskCache {
|
impl DiskCache {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
let cache_path = choose_app_strategy(crate::config::APP_STRATEGY.clone())
|
let cache_path = Paths::in_config_dir("githubcopilot/info.json");
|
||||||
.expect("goose requires a home dir")
|
|
||||||
.in_config_dir("githubcopilot/info.json");
|
|
||||||
Self { cache_path }
|
Self { cache_path }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
|
use crate::config::paths::Paths;
|
||||||
use anyhow::Result;
|
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 once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
@@ -38,12 +38,7 @@ struct TokenCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_base_path() -> PathBuf {
|
fn get_base_path() -> PathBuf {
|
||||||
// choose_app_strategy().config_dir()
|
Paths::in_config_dir("databricks/oauth")
|
||||||
// - 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")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TokenCache {
|
impl TokenCache {
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use etcetera::{choose_app_strategy, AppStrategy};
|
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::{Path, PathBuf};
|
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::read_recipe_file_content::{read_recipe_file, RecipeFile};
|
||||||
use crate::recipe::Recipe;
|
use crate::recipe::Recipe;
|
||||||
use crate::recipe::RECIPE_FILE_EXTENSIONS;
|
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 {
|
pub fn get_recipe_library_dir(is_global: bool) -> PathBuf {
|
||||||
if is_global {
|
if is_global {
|
||||||
choose_app_strategy(APP_STRATEGY.clone())
|
Paths::config_dir().join("recipes")
|
||||||
.expect("goose requires a home dir")
|
|
||||||
.config_dir()
|
|
||||||
.join("recipes")
|
|
||||||
} else {
|
} else {
|
||||||
std::env::current_dir().unwrap().join(".goose/recipes")
|
std::env::current_dir().unwrap().join(".goose/recipes")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,14 +7,14 @@ use std::sync::Arc;
|
|||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use etcetera::{choose_app_strategy, AppStrategy};
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
use tokio_cron_scheduler::{job::JobId, Job, JobScheduler as TokioJobScheduler};
|
use tokio_cron_scheduler::{job::JobId, Job, JobScheduler as TokioJobScheduler};
|
||||||
|
|
||||||
use crate::agents::AgentEvent;
|
use crate::agents::AgentEvent;
|
||||||
use crate::agents::{Agent, SessionConfig};
|
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::message::Message;
|
||||||
use crate::conversation::Conversation;
|
use crate::conversation::Conversation;
|
||||||
use crate::providers::base::Provider as GooseProvider; // Alias to avoid conflict in test section
|
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> {
|
pub fn get_default_scheduler_storage_path() -> Result<PathBuf, io::Error> {
|
||||||
let strategy = choose_app_strategy(config::APP_STRATEGY.clone())
|
let data_dir = Paths::data_dir();
|
||||||
.map_err(|e| io::Error::new(io::ErrorKind::NotFound, e.to_string()))?;
|
|
||||||
let data_dir = strategy.data_dir();
|
|
||||||
fs::create_dir_all(&data_dir)?;
|
fs::create_dir_all(&data_dir)?;
|
||||||
Ok(data_dir.join("schedules.json"))
|
Ok(data_dir.join("schedules.json"))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_default_scheduled_recipes_dir() -> Result<PathBuf, SchedulerError> {
|
pub fn get_default_scheduled_recipes_dir() -> Result<PathBuf, SchedulerError> {
|
||||||
let strategy = choose_app_strategy(config::APP_STRATEGY.clone()).map_err(|e| {
|
let data_dir = Paths::data_dir();
|
||||||
SchedulerError::StorageError(io::Error::new(io::ErrorKind::NotFound, e.to_string()))
|
|
||||||
})?;
|
|
||||||
let data_dir = strategy.data_dir();
|
|
||||||
let recipes_dir = data_dir.join("scheduled_recipes");
|
let recipes_dir = data_dir.join("scheduled_recipes");
|
||||||
fs::create_dir_all(&recipes_dir).map_err(SchedulerError::StorageError)?;
|
fs::create_dir_all(&recipes_dir).map_err(SchedulerError::StorageError)?;
|
||||||
tracing::debug!(
|
tracing::debug!(
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::config::APP_STRATEGY;
|
use crate::config::paths::Paths;
|
||||||
use crate::conversation::message::Message;
|
use crate::conversation::message::Message;
|
||||||
use crate::conversation::Conversation;
|
use crate::conversation::Conversation;
|
||||||
use crate::providers::base::{Provider, MSG_COUNT_FOR_SESSION_NAME_GENERATION};
|
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 crate::session::extension_data::ExtensionData;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use etcetera::{choose_app_strategy, AppStrategy};
|
|
||||||
use rmcp::model::Role;
|
use rmcp::model::Role;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sqlx::sqlite::SqliteConnectOptions;
|
use sqlx::sqlite::SqliteConnectOptions;
|
||||||
@@ -241,16 +240,13 @@ pub struct SessionStorage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn ensure_session_dir() -> Result<PathBuf> {
|
pub fn ensure_session_dir() -> Result<PathBuf> {
|
||||||
let data_dir = choose_app_strategy(APP_STRATEGY.clone())
|
let session_dir = Paths::data_dir().join("sessions");
|
||||||
.expect("goose requires a home dir")
|
|
||||||
.data_dir()
|
|
||||||
.join("sessions");
|
|
||||||
|
|
||||||
if !data_dir.exists() {
|
if !session_dir.exists() {
|
||||||
fs::create_dir_all(&data_dir)?;
|
fs::create_dir_all(&session_dir)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(data_dir)
|
Ok(session_dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn role_to_string(role: &Role) -> &'static str {
|
fn role_to_string(role: &Role) -> &'static str {
|
||||||
|
|||||||
@@ -151,6 +151,11 @@ async function ensureTempDirExists(): Promise<string> {
|
|||||||
|
|
||||||
if (started) app.quit();
|
if (started) app.quit();
|
||||||
|
|
||||||
|
if (process.env.ENABLE_PLAYWRIGHT) {
|
||||||
|
console.log('[Main] Enabling Playwright remote debugging on port 9222');
|
||||||
|
app.commandLine.appendSwitch('remote-debugging-port', '9222');
|
||||||
|
}
|
||||||
|
|
||||||
// In development mode, force registration as the default protocol client
|
// In development mode, force registration as the default protocol client
|
||||||
// In production, register normally
|
// In production, register normally
|
||||||
if (MAIN_WINDOW_VITE_DEV_SERVER_URL) {
|
if (MAIN_WINDOW_VITE_DEV_SERVER_URL) {
|
||||||
@@ -519,10 +524,9 @@ const createChat = async (
|
|||||||
const settings = loadSettings();
|
const settings = loadSettings();
|
||||||
updateSchedulingEngineEnvironment(settings.schedulingEngine);
|
updateSchedulingEngineEnvironment(settings.schedulingEngine);
|
||||||
|
|
||||||
// Start new Goosed process for regular windows
|
|
||||||
// Pass through scheduling engine environment variables
|
|
||||||
const envVars = {
|
const envVars = {
|
||||||
GOOSE_SCHEDULER_TYPE: process.env.GOOSE_SCHEDULER_TYPE,
|
GOOSE_SCHEDULER_TYPE: process.env.GOOSE_SCHEDULER_TYPE,
|
||||||
|
GOOSE_PATH_ROOT: process.env.GOOSE_PATH_ROOT,
|
||||||
};
|
};
|
||||||
const [newPort, newWorkingDir, newGoosedProcess] = await startGoosed(
|
const [newPort, newWorkingDir, newGoosedProcess] = await startGoosed(
|
||||||
app,
|
app,
|
||||||
@@ -1679,7 +1683,6 @@ async function appMain() {
|
|||||||
// Ensure Windows shims are available before any MCP processes are spawned
|
// Ensure Windows shims are available before any MCP processes are spawned
|
||||||
await ensureWinShims();
|
await ensureWinShims();
|
||||||
|
|
||||||
// Register update IPC handlers once (but don't setup auto-updater yet)
|
|
||||||
registerUpdateIpcHandlers();
|
registerUpdateIpcHandlers();
|
||||||
|
|
||||||
// Handle microphone permission requests
|
// Handle microphone permission requests
|
||||||
|
|||||||
Reference in New Issue
Block a user