mirror of
https://github.com/block/goose.git
synced 2026-07-17 12:56:20 +02:00
use GOOSE_PATH_ROOT to create isolated app state
This commit is contained in:
@@ -11,6 +11,10 @@ case "$mode" in
|
||||
esac
|
||||
|
||||
profile="${GOOSE2_PROFILE:-}"
|
||||
if [[ -n "$profile" && -z "${GOOSE_PATH_ROOT:-}" ]]; then
|
||||
export GOOSE_PATH_ROOT="${TMPDIR:-/tmp}goose2-${profile}"
|
||||
fi
|
||||
|
||||
port_key="$(pwd)::${profile:-default}"
|
||||
vite_port=$(
|
||||
PORT_KEY="$port_key" python3 -c \
|
||||
|
||||
@@ -7,6 +7,7 @@ use crate::services::acp::{
|
||||
make_composite_key, search_sessions_via_exports, AcpRunningSession, AcpService, AcpSessionInfo,
|
||||
AcpSessionRegistry, GooseAcpManager, SessionSearchResult,
|
||||
};
|
||||
use crate::services::goose_paths::goose_artifacts_dir;
|
||||
|
||||
const DEPRECATED_PROVIDER_IDS: &[&str] = &["claude-code", "codex", "gemini-cli"];
|
||||
|
||||
@@ -22,13 +23,6 @@ fn should_include_provider(provider_id: &str) -> bool {
|
||||
!DEPRECATED_PROVIDER_IDS.contains(&provider_id)
|
||||
}
|
||||
|
||||
fn default_artifacts_working_dir() -> PathBuf {
|
||||
if let Some(home_dir) = dirs::home_dir() {
|
||||
return home_dir.join(".goose").join("artifacts");
|
||||
}
|
||||
PathBuf::from("/tmp").join(".goose").join("artifacts")
|
||||
}
|
||||
|
||||
fn expand_home_dir(path: PathBuf) -> PathBuf {
|
||||
let path_string = path.to_string_lossy();
|
||||
|
||||
@@ -56,7 +50,7 @@ fn resolve_working_dir(
|
||||
.map(|dir| dir.trim().to_string())
|
||||
.filter(|dir| !dir.is_empty())
|
||||
.map(PathBuf::from)
|
||||
.unwrap_or_else(default_artifacts_working_dir);
|
||||
.unwrap_or_else(goose_artifacts_dir);
|
||||
let working_dir = expand_home_dir(working_dir);
|
||||
|
||||
let working_dir = if working_dir.is_relative() {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::services::goose_paths::goose_avatars_dir;
|
||||
use crate::services::personas::PersonaStore;
|
||||
use crate::types::agents::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -56,7 +57,7 @@ pub fn save_persona_avatar_bytes(
|
||||
/// Returns the absolute path to the avatars directory (~/.goose/avatars/).
|
||||
#[tauri::command]
|
||||
pub fn get_avatars_dir() -> String {
|
||||
PersonaStore::avatars_dir().to_string_lossy().to_string()
|
||||
goose_avatars_dir().to_string_lossy().to_string()
|
||||
}
|
||||
|
||||
// --- Sprout-compatible persona import/export ---
|
||||
|
||||
@@ -2,10 +2,7 @@ use serde::Deserialize;
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
fn projects_dir() -> Result<PathBuf, String> {
|
||||
let home = dirs::home_dir().ok_or("Could not determine home directory")?;
|
||||
Ok(home.join(".goose").join("projects"))
|
||||
}
|
||||
use crate::services::goose_paths::goose_projects_dir;
|
||||
|
||||
fn generate_id() -> String {
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
@@ -62,7 +59,7 @@ fn slugify(name: &str) -> String {
|
||||
/// Scan all project directories and find the one whose project.json has the given id.
|
||||
/// Returns (dir_path, ProjectInfo).
|
||||
fn find_project_by_id(id: &str) -> Result<(PathBuf, StoredProjectInfo), String> {
|
||||
let base = projects_dir()?;
|
||||
let base = goose_projects_dir();
|
||||
if !base.exists() {
|
||||
return Err(format!("Project with id \"{}\" not found", id));
|
||||
}
|
||||
@@ -195,7 +192,7 @@ fn project_info_from_stored(project_dir: &Path, stored: StoredProjectInfo) -> Pr
|
||||
|
||||
#[tauri::command]
|
||||
pub fn list_projects() -> Result<Vec<ProjectInfo>, String> {
|
||||
let dir = projects_dir()?;
|
||||
let dir = goose_projects_dir();
|
||||
|
||||
if !dir.exists() {
|
||||
return Ok(vec![]);
|
||||
@@ -233,7 +230,7 @@ pub fn list_projects() -> Result<Vec<ProjectInfo>, String> {
|
||||
|
||||
#[tauri::command]
|
||||
pub fn list_archived_projects() -> Result<Vec<ProjectInfo>, String> {
|
||||
let dir = projects_dir()?;
|
||||
let dir = goose_projects_dir();
|
||||
if !dir.exists() {
|
||||
return Ok(vec![]);
|
||||
}
|
||||
@@ -279,7 +276,7 @@ pub fn create_project(
|
||||
return Err("Project name must not be empty".to_string());
|
||||
}
|
||||
|
||||
let base = projects_dir()?;
|
||||
let base = goose_projects_dir();
|
||||
let slug = slugify(&name);
|
||||
|
||||
// Determine final directory name, avoiding collisions
|
||||
@@ -392,7 +389,7 @@ pub fn get_project(id: String) -> Result<ProjectInfo, String> {
|
||||
|
||||
#[tauri::command]
|
||||
pub fn archive_project(id: String) -> Result<(), String> {
|
||||
let base = projects_dir()?;
|
||||
let base = goose_projects_dir();
|
||||
if !base.exists() {
|
||||
return Err("Projects directory not found".to_string());
|
||||
}
|
||||
@@ -425,7 +422,7 @@ pub fn archive_project(id: String) -> Result<(), String> {
|
||||
|
||||
#[tauri::command]
|
||||
pub fn restore_project(id: String) -> Result<(), String> {
|
||||
let base = projects_dir()?;
|
||||
let base = goose_projects_dir();
|
||||
if !base.exists() {
|
||||
return Err("Projects directory not found".to_string());
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ use tokio::process::{Child, Command};
|
||||
use tokio::sync::OnceCell;
|
||||
use tokio_tungstenite::connect_async;
|
||||
|
||||
use crate::services::goose_paths::goose_artifacts_dir;
|
||||
|
||||
const GOOSE_SERVE_CONNECT_TIMEOUT: Duration = Duration::from_secs(30);
|
||||
const GOOSE_SERVE_CONNECT_RETRY_DELAY: Duration = Duration::from_millis(100);
|
||||
const LOCALHOST: &str = "127.0.0.1";
|
||||
@@ -61,7 +63,7 @@ impl GooseServeProcess {
|
||||
|
||||
// Use a stable working directory for the long-lived server process.
|
||||
// Individual sessions will set their own cwd via the ACP protocol.
|
||||
let working_dir = default_serve_working_dir();
|
||||
let working_dir = goose_artifacts_dir();
|
||||
std::fs::create_dir_all(&working_dir).map_err(|e| {
|
||||
format!(
|
||||
"Failed to create goose serve working directory {}: {e}",
|
||||
@@ -147,13 +149,6 @@ async fn wait_for_server_ready(ws_url: &str, child: &mut Child) -> Result<(), St
|
||||
}
|
||||
}
|
||||
|
||||
fn default_serve_working_dir() -> PathBuf {
|
||||
dirs::home_dir()
|
||||
.unwrap_or_else(|| PathBuf::from("/tmp"))
|
||||
.join(".goose")
|
||||
.join("artifacts")
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Binary resolution
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub fn goose_home_dir() -> PathBuf {
|
||||
if let Ok(root) = std::env::var("GOOSE_PATH_ROOT") {
|
||||
return PathBuf::from(root);
|
||||
}
|
||||
|
||||
dirs::home_dir()
|
||||
.unwrap_or_else(|| PathBuf::from("/tmp"))
|
||||
.join(".goose")
|
||||
}
|
||||
|
||||
pub fn goose_artifacts_dir() -> PathBuf {
|
||||
goose_home_dir().join("artifacts")
|
||||
}
|
||||
|
||||
pub fn goose_projects_dir() -> PathBuf {
|
||||
goose_home_dir().join("projects")
|
||||
}
|
||||
|
||||
pub fn goose_personas_path() -> PathBuf {
|
||||
goose_home_dir().join("personas.json")
|
||||
}
|
||||
|
||||
pub fn goose_agents_dir() -> PathBuf {
|
||||
goose_home_dir().join("agents")
|
||||
}
|
||||
|
||||
pub fn goose_avatars_dir() -> PathBuf {
|
||||
goose_home_dir().join("avatars")
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
pub mod acp;
|
||||
pub mod goose_config;
|
||||
pub mod goose_paths;
|
||||
pub mod personas;
|
||||
pub(crate) mod provider_defs;
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
use crate::types::agents::{
|
||||
builtin_personas, Avatar, CreatePersonaRequest, Persona, UpdatePersonaRequest,
|
||||
};
|
||||
use crate::services::goose_paths::{
|
||||
goose_agents_dir, goose_avatars_dir, goose_personas_path,
|
||||
};
|
||||
use log::warn;
|
||||
use std::collections::HashSet;
|
||||
use std::path::PathBuf;
|
||||
@@ -20,7 +23,7 @@ struct MarkdownFrontmatter {
|
||||
|
||||
impl PersonaStore {
|
||||
pub fn new() -> Self {
|
||||
let store_path = Self::store_path();
|
||||
let store_path = goose_personas_path();
|
||||
let stored = Self::load_from_disk(&store_path);
|
||||
let markdown = Self::load_markdown_personas();
|
||||
let merged = Self::merge_all(stored, markdown);
|
||||
@@ -30,19 +33,6 @@ impl PersonaStore {
|
||||
}
|
||||
}
|
||||
|
||||
fn store_path() -> PathBuf {
|
||||
let base = dirs::home_dir().expect("home dir");
|
||||
base.join(".goose").join("personas.json")
|
||||
}
|
||||
|
||||
/// Path to the avatars directory (~/.goose/avatars/).
|
||||
pub fn avatars_dir() -> PathBuf {
|
||||
dirs::home_dir()
|
||||
.expect("home dir")
|
||||
.join(".goose")
|
||||
.join("avatars")
|
||||
}
|
||||
|
||||
fn load_from_disk(path: &PathBuf) -> Vec<Persona> {
|
||||
match std::fs::read_to_string(path) {
|
||||
Ok(contents) => serde_json::from_str(&contents).unwrap_or_default(),
|
||||
@@ -86,17 +76,9 @@ impl PersonaStore {
|
||||
result
|
||||
}
|
||||
|
||||
/// Directory containing markdown persona files.
|
||||
fn agents_dir() -> PathBuf {
|
||||
dirs::home_dir()
|
||||
.expect("home dir")
|
||||
.join(".goose")
|
||||
.join("agents")
|
||||
}
|
||||
|
||||
/// Scan `~/.goose/agents/*.md` and parse each into a Persona.
|
||||
fn load_markdown_personas() -> Vec<Persona> {
|
||||
let dir = Self::agents_dir();
|
||||
let dir = goose_agents_dir();
|
||||
if !dir.is_dir() {
|
||||
return Vec::new();
|
||||
}
|
||||
@@ -309,7 +291,7 @@ impl PersonaStore {
|
||||
|
||||
// Clean up local avatar file if present
|
||||
if let Some(Avatar::Local(filename)) = &persona.avatar {
|
||||
let path = Self::avatars_dir().join(filename);
|
||||
let path = goose_avatars_dir().join(filename);
|
||||
let _ = std::fs::remove_file(path);
|
||||
}
|
||||
|
||||
@@ -321,7 +303,7 @@ impl PersonaStore {
|
||||
/// Copy an avatar image from a source path to ~/.goose/avatars/{persona_id}.{ext}.
|
||||
/// Returns the filename (not full path).
|
||||
pub fn save_avatar_from_path(persona_id: &str, source_path: &str) -> Result<String, String> {
|
||||
let avatars_dir = Self::avatars_dir();
|
||||
let avatars_dir = goose_avatars_dir();
|
||||
std::fs::create_dir_all(&avatars_dir)
|
||||
.map_err(|e| format!("Failed to create avatars directory: {}", e))?;
|
||||
|
||||
@@ -362,7 +344,7 @@ impl PersonaStore {
|
||||
bytes: &[u8],
|
||||
extension: &str,
|
||||
) -> Result<String, String> {
|
||||
let avatars_dir = Self::avatars_dir();
|
||||
let avatars_dir = goose_avatars_dir();
|
||||
std::fs::create_dir_all(&avatars_dir)
|
||||
.map_err(|e| format!("Failed to create avatars directory: {}", e))?;
|
||||
|
||||
@@ -391,7 +373,7 @@ impl PersonaStore {
|
||||
/// Delete avatar file for a persona.
|
||||
#[allow(dead_code)]
|
||||
pub fn delete_avatar_file(filename: &str) {
|
||||
let path = Self::avatars_dir().join(filename);
|
||||
let path = goose_avatars_dir().join(filename);
|
||||
let _ = std::fs::remove_file(path);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user