Files
hikari-desktop/src-tauri/src/bridge_manager.rs
T
2026-02-04 13:19:10 -08:00

182 lines
5.5 KiB
Rust

use parking_lot::Mutex;
use std::collections::HashMap;
use std::sync::Arc;
use tauri::AppHandle;
use crate::config::ClaudeStartOptions;
use crate::provider_bridge::ProviderBridge;
use crate::stats::UsageStats;
pub struct BridgeManager {
bridges: HashMap<String, ProviderBridge>,
app_handle: Option<AppHandle>,
}
impl BridgeManager {
pub fn new() -> Self {
BridgeManager {
bridges: HashMap::new(),
app_handle: None,
}
}
pub fn set_app_handle(&mut self, app: AppHandle) {
self.app_handle = Some(app);
}
pub fn start_claude(
&mut self,
conversation_id: &str,
options: ClaudeStartOptions,
) -> Result<(), String> {
// Check if a bridge already exists and is running for this conversation
if self
.bridges
.get(conversation_id)
.map(|b| b.is_running())
.unwrap_or(false)
{
return Err("Claude is already running for this conversation".to_string());
}
let app = self
.app_handle
.as_ref()
.ok_or_else(|| "App handle not set".to_string())?
.clone();
// Check if existing bridge matches the requested provider type
// If provider type changed, create a new bridge
let should_recreate = self.bridges.get(conversation_id).map_or(false, |bridge| {
bridge.provider_type() != options.provider_type
});
if should_recreate {
// Remove existing bridge if provider type changed
self.bridges.remove(conversation_id);
}
// Reuse existing bridge if it exists (preserves stats across reconnects)
// Only create a new bridge if one doesn't exist for this conversation
let provider_type = options.provider_type;
let bridge = self
.bridges
.entry(conversation_id.to_string())
.or_insert_with(|| ProviderBridge::new(provider_type, conversation_id.to_string()));
// Start the Claude process
bridge.start(app, options)?;
Ok(())
}
pub fn stop_claude(&mut self, conversation_id: &str) -> Result<(), String> {
if let Some(bridge) = self.bridges.get_mut(conversation_id) {
let app = self
.app_handle
.as_ref()
.ok_or_else(|| "App handle not set".to_string())?;
bridge.stop(app);
Ok(())
} else {
Err("No Claude instance found for this conversation".to_string())
}
}
pub fn interrupt_claude(&mut self, conversation_id: &str) -> Result<(), String> {
if let Some(bridge) = self.bridges.get_mut(conversation_id) {
let app = self
.app_handle
.as_ref()
.ok_or_else(|| "App handle not set".to_string())?;
bridge.interrupt(app)
} else {
Err("No Claude instance found for this conversation".to_string())
}
}
pub fn send_prompt(&mut self, conversation_id: &str, message: String) -> Result<(), String> {
if let Some(bridge) = self.bridges.get_mut(conversation_id) {
bridge.send_message(&message)
} else {
Err("No Claude instance found for this conversation".to_string())
}
}
pub fn send_tool_result(
&mut self,
conversation_id: &str,
tool_use_id: &str,
result: serde_json::Value,
) -> Result<(), String> {
if let Some(bridge) = self.bridges.get_mut(conversation_id) {
bridge.send_tool_result(tool_use_id, result)
} else {
Err("No Claude instance found for this conversation".to_string())
}
}
pub fn is_claude_running(&self, conversation_id: &str) -> bool {
self.bridges
.get(conversation_id)
.map(|b| b.is_running())
.unwrap_or(false)
}
pub fn get_working_directory(&self, conversation_id: &str) -> Result<String, String> {
self.bridges
.get(conversation_id)
.map(|b| b.get_working_directory().to_string())
.ok_or_else(|| "No Claude instance found for this conversation".to_string())
}
pub fn get_usage_stats(&self, conversation_id: &str) -> Result<UsageStats, String> {
self.bridges
.get(conversation_id)
.map(|b| b.get_stats())
.ok_or_else(|| "No Claude instance found for this conversation".to_string())
}
#[allow(dead_code)]
pub fn cleanup_stopped_bridges(&mut self) {
// Remove bridges that are no longer running
self.bridges.retain(|_, bridge| bridge.is_running());
}
#[allow(dead_code)]
pub fn stop_all(&mut self) {
if let Some(app) = &self.app_handle {
for (_, bridge) in self.bridges.iter_mut() {
bridge.stop(app);
}
}
self.bridges.clear();
}
#[allow(dead_code)]
pub fn get_active_conversations(&self) -> Vec<String> {
self.bridges
.keys()
.filter(|id| {
self.bridges
.get(*id)
.map(|b| b.is_running())
.unwrap_or(false)
})
.cloned()
.collect()
}
}
impl Default for BridgeManager {
fn default() -> Self {
Self::new()
}
}
pub type SharedBridgeManager = Arc<Mutex<BridgeManager>>;
pub fn create_shared_bridge_manager() -> SharedBridgeManager {
Arc::new(Mutex::new(BridgeManager::new()))
}