Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion src/apps/desktop/src/api/app_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use bitfun_core::agentic::{agents, tools};
use bitfun_core::infrastructure::ai::{AIClient, AIClientFactory};
use bitfun_core::miniapp::{initialize_global_miniapp_manager, MiniAppManager, JsWorkerPool};
use bitfun_core::miniapp::{initialize_global_miniapp_manager, JsWorkerPool, MiniAppManager};
use bitfun_core::service::{ai_rules, config, filesystem, mcp, token_usage, workspace};
use bitfun_core::util::errors::*;

Expand Down Expand Up @@ -120,6 +120,18 @@ impl AppState {
miniapp_manager
.set_workspace_path(Some(workspace_path.clone()))
.await;
if let Err(e) = bitfun_core::service::snapshot::initialize_global_snapshot_manager(
workspace_path.clone(),
None,
)
.await
{
log::warn!(
"Failed to restore snapshot system on startup: path={}, error={}",
workspace_path.display(),
e
);
}
if let Err(e) = ai_rules_service.set_workspace(workspace_path).await {
log::warn!("Failed to restore AI rules workspace on startup: {}", e);
}
Expand Down
93 changes: 51 additions & 42 deletions src/apps/desktop/src/api/snapshot_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

use bitfun_core::infrastructure::{get_workspace_path, try_get_path_manager_arc};
use bitfun_core::service::snapshot::{
ensure_global_snapshot_manager, initialize_global_snapshot_manager, OperationType,
SnapshotConfig,
ensure_global_snapshot_manager, get_global_snapshot_manager,
initialize_global_snapshot_manager, OperationType, SnapshotConfig, SnapshotManager,
};
use log::warn;
use log::{info, warn};
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use std::{path::PathBuf, sync::Arc};
use tauri::{AppHandle, Emitter};

#[derive(Debug, Clone, Serialize, Deserialize)]
Expand Down Expand Up @@ -140,13 +140,40 @@ pub async fn initialize_snapshot(
}))
}

async fn ensure_snapshot_manager_ready() -> Result<Arc<SnapshotManager>, String> {
if let Some(manager) = get_global_snapshot_manager() {
return Ok(manager);
}

let workspace_path = get_workspace_path().ok_or_else(|| {
"Failed to get snapshot manager: no active workspace available to initialize snapshot system"
.to_string()
})?;

info!(
"Snapshot manager missing, initializing lazily: workspace={}",
workspace_path.display()
);

initialize_global_snapshot_manager(workspace_path.clone(), None)
.await
.map_err(|e| {
format!(
"Failed to initialize snapshot system for workspace {}: {}",
workspace_path.display(),
e
)
})?;

ensure_global_snapshot_manager().map_err(|e| format!("Failed to get snapshot manager: {}", e))
}

#[tauri::command]
pub async fn record_file_change(
app_handle: AppHandle,
request: RecordFileChangeRequest,
) -> Result<String, String> {
let manager = ensure_global_snapshot_manager()
.map_err(|e| format!("Failed to get snapshot manager: {}", e))?;
let manager = ensure_snapshot_manager_ready().await?;

let operation_type = match request.operation_type.as_str() {
"Create" => OperationType::Create,
Expand Down Expand Up @@ -190,8 +217,7 @@ pub async fn rollback_session(
app_handle: AppHandle,
request: RollbackSessionRequest,
) -> Result<Vec<String>, String> {
let manager = ensure_global_snapshot_manager()
.map_err(|e| format!("Failed to get snapshot manager: {}", e))?;
let manager = ensure_snapshot_manager_ready().await?;

let restored_files = manager
.rollback_session(&request.session_id)
Expand Down Expand Up @@ -220,8 +246,7 @@ pub async fn rollback_to_turn(
app_handle: AppHandle,
request: RollbackTurnRequest,
) -> Result<Vec<String>, String> {
let manager = ensure_global_snapshot_manager()
.map_err(|e| format!("Failed to get snapshot manager: {}", e))?;
let manager = ensure_snapshot_manager_ready().await?;

let restored_files = manager
.rollback_to_turn(&request.session_id, request.turn_index)
Expand Down Expand Up @@ -316,8 +341,7 @@ pub async fn accept_session(
app_handle: AppHandle,
request: AcceptSessionRequest,
) -> Result<serde_json::Value, String> {
let manager = ensure_global_snapshot_manager()
.map_err(|e| format!("Failed to get snapshot manager: {}", e))?;
let manager = ensure_snapshot_manager_ready().await?;

manager
.accept_session(&request.session_id)
Expand All @@ -342,8 +366,7 @@ pub async fn accept_file(
app_handle: AppHandle,
request: AcceptFileRequest,
) -> Result<serde_json::Value, String> {
let manager = ensure_global_snapshot_manager()
.map_err(|e| format!("Failed to get snapshot manager: {}", e))?;
let manager = ensure_snapshot_manager_ready().await?;

manager
.accept_file(&request.session_id, &request.file_path)
Expand All @@ -366,8 +389,7 @@ pub async fn accept_file(

#[tauri::command]
pub async fn get_session_files(request: GetSessionFilesRequest) -> Result<Vec<String>, String> {
let manager = ensure_global_snapshot_manager()
.map_err(|e| format!("Failed to get snapshot manager: {}", e))?;
let manager = ensure_snapshot_manager_ready().await?;

let files = manager
.get_session_files(&request.session_id)
Expand Down Expand Up @@ -412,8 +434,7 @@ pub async fn get_session_turns(
}
}

let manager = ensure_global_snapshot_manager()
.map_err(|e| format!("Failed to get snapshot manager: {}", e))?;
let manager = ensure_snapshot_manager_ready().await?;

let turns = manager
.get_session_turns(&request.session_id)
Expand All @@ -425,8 +446,7 @@ pub async fn get_session_turns(

#[tauri::command]
pub async fn get_turn_files(request: GetTurnFilesRequest) -> Result<Vec<String>, String> {
let manager = ensure_global_snapshot_manager()
.map_err(|e| format!("Failed to get snapshot manager: {}", e))?;
let manager = ensure_snapshot_manager_ready().await?;

let files = manager
.get_turn_files(&request.session_id, request.turn_index)
Expand All @@ -441,8 +461,7 @@ pub async fn get_turn_files(request: GetTurnFilesRequest) -> Result<Vec<String>,

#[tauri::command]
pub async fn get_file_diff(request: GetFileDiffRequest) -> Result<serde_json::Value, String> {
let manager = ensure_global_snapshot_manager()
.map_err(|e| format!("Failed to get snapshot manager: {}", e))?;
let manager = ensure_snapshot_manager_ready().await?;

let diff = manager
.get_file_diff(
Expand All @@ -460,8 +479,7 @@ pub async fn get_file_diff(request: GetFileDiffRequest) -> Result<serde_json::Va
pub async fn get_operation_diff(
request: GetOperationDiffRequest,
) -> Result<serde_json::Value, String> {
let manager = ensure_global_snapshot_manager()
.map_err(|e| format!("Failed to get snapshot manager: {}", e))?;
let manager = ensure_snapshot_manager_ready().await?;

let diff = manager
.get_file_diff(
Expand All @@ -484,8 +502,7 @@ pub async fn get_operation_diff(
pub async fn get_operation_summary(
request: GetOperationSummaryRequest,
) -> Result<serde_json::Value, String> {
let manager = ensure_global_snapshot_manager()
.map_err(|e| format!("Failed to get snapshot manager: {}", e))?;
let manager = ensure_snapshot_manager_ready().await?;

let summary = manager
.get_operation_summary(&request.sessionId, &request.operationId)
Expand All @@ -509,8 +526,7 @@ pub async fn get_operation_summary(
pub async fn get_session_stats(
request: GetSessionStatsRequest,
) -> Result<serde_json::Value, String> {
let manager = ensure_global_snapshot_manager()
.map_err(|e| format!("Failed to get snapshot manager: {}", e))?;
let manager = ensure_snapshot_manager_ready().await?;

let stats = manager
.get_session_stats(&request.session_id)
Expand All @@ -522,8 +538,7 @@ pub async fn get_session_stats(

#[tauri::command]
pub async fn get_snapshot_system_stats() -> Result<serde_json::Value, String> {
let manager = ensure_global_snapshot_manager()
.map_err(|e| format!("Failed to get snapshot manager: {}", e))?;
let manager = ensure_snapshot_manager_ready().await?;

let stats = manager
.get_system_stats()
Expand All @@ -541,8 +556,7 @@ pub struct CleanupSnapshotDataRequest {

#[tauri::command]
pub async fn get_snapshot_sessions() -> Result<Vec<String>, String> {
let manager = ensure_global_snapshot_manager()
.map_err(|e| format!("Failed to get snapshot manager: {}", e))?;
let manager = ensure_snapshot_manager_ready().await?;

manager
.list_sessions()
Expand All @@ -554,8 +568,7 @@ pub async fn get_snapshot_sessions() -> Result<Vec<String>, String> {
pub async fn cleanup_snapshot_data(
request: CleanupSnapshotDataRequest,
) -> Result<serde_json::Value, String> {
let manager = ensure_global_snapshot_manager()
.map_err(|e| format!("Failed to get snapshot manager: {}", e))?;
let manager = ensure_snapshot_manager_ready().await?;

manager
.cleanup_snapshot_data(request.max_age_days)
Expand All @@ -571,8 +584,7 @@ pub async fn cleanup_snapshot_data(

#[tauri::command]
pub async fn check_git_isolation() -> Result<serde_json::Value, String> {
let manager = ensure_global_snapshot_manager()
.map_err(|e| format!("Failed to get snapshot manager: {}", e))?;
let manager = ensure_snapshot_manager_ready().await?;

let is_isolated = manager
.check_git_isolation()
Expand All @@ -589,8 +601,7 @@ pub async fn check_git_isolation() -> Result<serde_json::Value, String> {
pub async fn get_file_change_history(
request: GetFileChangeHistoryRequest,
) -> Result<serde_json::Value, String> {
let manager = ensure_global_snapshot_manager()
.map_err(|e| format!("Failed to get snapshot manager: {}", e))?;
let manager = ensure_snapshot_manager_ready().await?;

let file_path = PathBuf::from(&request.file_path);
let changes = manager
Expand All @@ -605,8 +616,7 @@ pub async fn get_file_change_history(
pub async fn get_all_modified_files(
_request: GetAllModifiedFilesRequest,
) -> Result<Vec<String>, String> {
let manager = ensure_global_snapshot_manager()
.map_err(|e| format!("Failed to get snapshot manager: {}", e))?;
let manager = ensure_snapshot_manager_ready().await?;

let files = manager
.get_all_modified_files()
Expand All @@ -623,8 +633,7 @@ pub async fn get_all_modified_files(
pub async fn get_baseline_snapshot_diff(
request: GetBaselineSnapshotDiffRequest,
) -> Result<serde_json::Value, String> {
let manager = ensure_global_snapshot_manager()
.map_err(|e| format!("Failed to get snapshot manager: {}", e))?;
let manager = ensure_snapshot_manager_ready().await?;

let file_path = PathBuf::from(&request.file_path);

Expand Down