diff --git a/src/auto_task/app_generator.rs b/src/auto_task/app_generator.rs index 0905bdd7f..bc101e4cd 100644 --- a/src/auto_task/app_generator.rs +++ b/src/auto_task/app_generator.rs @@ -1552,7 +1552,7 @@ impl AppGenerator { ); for (idx, tool) in llm_app.tools.iter().enumerate() { - let tool_path = format!(".gbdialog/tools/{}", tool.filename); + let tool_path = format!("{}.gbdialog/tools/{}", llm_app.name, tool.filename); self.files_written.push(format!("tools/{}", tool.filename)); self.bytes_generated += tool.content.len() as u64; @@ -1624,7 +1624,7 @@ impl AppGenerator { ); for (idx, scheduler) in llm_app.schedulers.iter().enumerate() { - let scheduler_path = format!(".gbdialog/schedulers/{}", scheduler.filename); + let scheduler_path = format!("{}.gbdialog/schedulers/{}", llm_app.name, scheduler.filename); self.files_written .push(format!("schedulers/{}", scheduler.filename)); self.bytes_generated += scheduler.content.len() as u64; diff --git a/src/auto_task/designer_ai.rs b/src/auto_task/designer_ai.rs index 4bb98131b..4a9ad2dfe 100644 --- a/src/auto_task/designer_ai.rs +++ b/src/auto_task/designer_ai.rs @@ -330,8 +330,8 @@ Guidelines: - STYLE: Changes to CSS files (colors, layout, fonts, spacing) - HTML: Changes to HTML structure (forms, buttons, elements) - DATABASE: Adding fields to tables.bas or creating new tables -- TOOL: Creating/modifying .gbdialog/tools/*.bas files -- SCHEDULER: Creating/modifying .gbdialog/schedulers/*.bas files +- TOOL: Creating/modifying {botname}.gbdialog/tools/*.bas files +- SCHEDULER: Creating/modifying {botname}.gbdialog/schedulers/*.bas files - Require confirmation for: deletions, bulk changes, database schema changes - Use the current_app and current_page context to determine which files to modify @@ -432,7 +432,7 @@ Respond ONLY with valid JSON."# { ( ModificationType::Tool, - ".gbdialog/tools/new-tool.bas".to_string(), + "{botname}.gbdialog/tools/new-tool.bas".to_string(), ) } else if lower.contains("schedule") || lower.contains("every day") @@ -441,7 +441,7 @@ Respond ONLY with valid JSON."# { ( ModificationType::Scheduler, - ".gbdialog/schedulers/new-scheduler.bas".to_string(), + "{botname}.gbdialog/schedulers/new-scheduler.bas".to_string(), ) } else { (ModificationType::Unknown, "".to_string()) @@ -762,7 +762,7 @@ Respond ONLY with valid JSON."# session: &UserSession, ) -> Result, Box> { let site_path = self.get_site_path(); - let tools_path = format!("{}/{}.gbai/.gbdialog/tools", site_path, session.bot_id); + let tools_path = format!("{}/{}.gbai/{}.gbdialog/tools", site_path, session.bot_id, session.bot_id); let mut tools = Vec::new(); if let Ok(entries) = std::fs::read_dir(&tools_path) { @@ -783,7 +783,7 @@ Respond ONLY with valid JSON."# session: &UserSession, ) -> Result, Box> { let site_path = self.get_site_path(); - let schedulers_path = format!("{}/{}.gbai/.gbdialog/schedulers", site_path, session.bot_id); + let schedulers_path = format!("{}/{}.gbai/{}.gbdialog/schedulers", site_path, session.bot_id, session.bot_id); let mut schedulers = Vec::new(); if let Ok(entries) = std::fs::read_dir(&schedulers_path) { diff --git a/src/auto_task/intent_classifier.rs b/src/auto_task/intent_classifier.rs index d36721f7a..d34ebf72e 100644 --- a/src/auto_task/intent_classifier.rs +++ b/src/auto_task/intent_classifier.rs @@ -677,8 +677,8 @@ END ON Utc::now().format("%Y-%m-%d %H:%M") ); - // Save to .gbdialog/events/ - let event_path = format!(".gbdialog/events/{handler_name}"); + // Save to {bot_id}.gbdialog/events/ + let event_path = format!("{}.gbdialog/events/{handler_name}", session.bot_id); self.save_basic_file(session.bot_id, &event_path, &basic_code)?; Ok(IntentResult { @@ -889,8 +889,8 @@ END SCHEDULE classification.original_text ); - // Save to .gbdialog/schedulers/ - let scheduler_path = format!(".gbdialog/schedulers/{scheduler_file}"); + // Save to {bot_id}.gbdialog/schedulers/ + let scheduler_path = format!("{}.gbdialog/schedulers/{scheduler_file}", session.bot_id); self.save_basic_file(session.bot_id, &scheduler_path, &basic_code)?; let schedule_id = Uuid::new_v4(); @@ -962,7 +962,7 @@ END GOAL // Save to .gbdialog/goals/ let goal_file = format!("{}.bas", goal_name.to_lowercase().replace(' ', "-")); - let goal_path = format!(".gbdialog/goals/{goal_file}"); + let goal_path = format!("{}.gbdialog/goals/{goal_file}", session.bot_id); self.save_basic_file(session.bot_id, &goal_path, &basic_code)?; Ok(IntentResult { @@ -1029,8 +1029,8 @@ END TRIGGER classification.original_text ); - // Save to .gbdialog/tools/ - let tool_path = format!(".gbdialog/tools/{tool_file}"); + // Save to {bot_id}.gbdialog/tools/ + let tool_path = format!("{}.gbdialog/tools/{tool_file}", session.bot_id); self.save_basic_file(session.bot_id, &tool_path, &basic_code)?; Ok(IntentResult { diff --git a/src/basic/keywords/use_tool.rs b/src/basic/keywords/use_tool.rs index 8d63c91d3..d65b26184 100644 --- a/src/basic/keywords/use_tool.rs +++ b/src/basic/keywords/use_tool.rs @@ -19,12 +19,15 @@ pub fn use_tool_keyword(state: Arc, user: UserSession, engine: &mut En tool_path_str, user_clone.id ); - let tool_name = tool_path_str - .strip_prefix(".gbdialog/") - .unwrap_or(&tool_path_str) - .strip_suffix(".bas") - .unwrap_or(&tool_path_str) - .to_string(); + // Strip {bot_name}.gbdialog/ or .gbdialog/ prefix, and .bas suffix + let tool_name = if let Some(idx) = tool_path_str.find(".gbdialog/") { + tool_path_str[idx + 10..] // Skip past ".gbdialog/" + } else { + &tool_path_str + } + .strip_suffix(".bas") + .unwrap_or_else(|| &tool_path_str) + .to_string(); if tool_name.is_empty() { return Err(Box::new(rhai::EvalAltResult::ErrorRuntime( "Invalid tool name".into(), @@ -86,12 +89,15 @@ pub fn use_tool_keyword(state: Arc, user: UserSession, engine: &mut En tool_path_str, user_clone2.id ); - let tool_name = tool_path_str - .strip_prefix(".gbdialog/") - .unwrap_or(&tool_path_str) - .strip_suffix(".bas") - .unwrap_or(&tool_path_str) - .to_string(); + // Strip {bot_name}.gbdialog/ or .gbdialog/ prefix, and .bas suffix + let tool_name = if let Some(idx) = tool_path_str.find(".gbdialog/") { + &tool_path_str[idx + 10..] // Skip past ".gbdialog/" + } else { + &tool_path_str + } + .strip_suffix(".bas") + .unwrap_or_else(|| &tool_path_str) + .to_string(); if tool_name.is_empty() { return Dynamic::from("ERROR: Invalid tool name"); } @@ -140,12 +146,15 @@ pub fn use_tool_keyword(state: Arc, user: UserSession, engine: &mut En tool_path_str, user_clone3.id ); - let tool_name = tool_path_str - .strip_prefix(".gbdialog/") - .unwrap_or(&tool_path_str) - .strip_suffix(".bas") - .unwrap_or(&tool_path_str) - .to_string(); + // Strip {bot_name}.gbdialog/ or .gbdialog/ prefix, and .bas suffix + let tool_name = if let Some(idx) = tool_path_str.find(".gbdialog/") { + &tool_path_str[idx + 10..] // Skip past ".gbdialog/" + } else { + &tool_path_str + } + .strip_suffix(".bas") + .unwrap_or_else(|| &tool_path_str) + .to_string(); if tool_name.is_empty() { return Dynamic::from("ERROR: Invalid tool name"); } diff --git a/src/core/bot/mod.rs b/src/core/bot/mod.rs index 4a18a8142..e4b6bf62b 100644 --- a/src/core/bot/mod.rs +++ b/src/core/bot/mod.rs @@ -227,8 +227,7 @@ impl BotOrchestrator { let mut bots_mounted = 0; let mut bots_created = 0; - let home_dir = std::env::var("HOME").unwrap_or_else(|_| ".".to_string()); - let data_dir = format!("{}/data", home_dir); + let data_dir = "/opt/gbo/data"; let directories_to_scan: Vec = vec![ self.state @@ -293,9 +292,9 @@ impl BotOrchestrator { *bots_mounted += 1; } Ok(false) => { - // Auto-create bots found in ~/data + // Auto-create bots found in /opt/gbo/data if dir_path.to_string_lossy().contains("/data") { - info!("Auto-creating bot '{}' from ~/data", bot_name); + info!("Auto-creating bot '{}' from /opt/gbo/data", bot_name); match self.create_bot_simple(bot_name) { Ok(_) => { info!("Bot '{}' created successfully", bot_name); @@ -489,8 +488,7 @@ impl BotOrchestrator { if should_execute_start_bas { // Always execute start.bas for this session (blocking - wait for completion) - let home_dir = std::env::var("HOME").unwrap_or_else(|_| ".".to_string()); - let data_dir = format!("{}/data", home_dir); + let data_dir = "/opt/gbo/data"; let start_script_path = format!("{}/{}.gbai/{}.gbdialog/start.bas", data_dir, bot_name_for_context, bot_name_for_context); info!("[START_BAS] Executing start.bas for session {} at: {}", actual_session_id, start_script_path); @@ -1088,8 +1086,7 @@ async fn handle_websocket( }; if should_execute_start_bas { - let home_dir = std::env::var("HOME").unwrap_or_else(|_| ".".to_string()); - let data_dir = format!("{}/data", home_dir); + let data_dir = "/opt/gbo/data"; let start_script_path = format!("{}/{}.gbai/{}.gbdialog/start.bas", data_dir, bot_name, bot_name); info!("Looking for start.bas at: {}", start_script_path); diff --git a/src/core/kb/website_crawler_service.rs b/src/core/kb/website_crawler_service.rs index e6d6f1914..0ad858ce6 100644 --- a/src/core/kb/website_crawler_service.rs +++ b/src/core/kb/website_crawler_service.rs @@ -352,7 +352,7 @@ impl WebsiteCrawlerService { Err(_) => continue, // Skip if bot not found }; - // Scan .gbdialog directory for .bas files + // Scan {bot_name}.gbdialog directory for .bas files let dialog_dir = path.join(format!("{}.gbdialog", bot_name)); if dialog_dir.exists() { self.scan_directory_for_websites(&dialog_dir, bot_id, &mut conn)?; diff --git a/src/drive/drive_monitor/mod.rs b/src/drive/drive_monitor/mod.rs index 407d4998d..2a7393adb 100644 --- a/src/drive/drive_monitor/mod.rs +++ b/src/drive/drive_monitor/mod.rs @@ -462,7 +462,7 @@ impl DriveMonitor { &self, client: &Client, ) -> Result<(), Box> { - let prefix = ".gbdialog/"; + // No prefix filter - list all and filter by *.gbdialog pattern below let mut current_files = HashMap::new(); let mut continuation_token = None; loop { @@ -486,6 +486,7 @@ impl DriveMonitor { for obj in list_objects.contents.unwrap_or_default() { let path = obj.key().unwrap_or_default().to_string(); let path_parts: Vec<&str> = path.split('/').collect(); + // Filter for paths matching *.gbdialog/*.bas pattern if path_parts.len() < 2 || !path_parts[0].ends_with(".gbdialog") { continue; } diff --git a/src/drive/local_file_monitor.rs b/src/drive/local_file_monitor.rs index db28a15be..5f72f995e 100644 --- a/src/drive/local_file_monitor.rs +++ b/src/drive/local_file_monitor.rs @@ -30,10 +30,8 @@ pub struct LocalFileMonitor { impl LocalFileMonitor { pub fn new(state: Arc) -> Self { - // Use ~/data as the base directory - let data_dir = PathBuf::from(std::env::var("HOME") - .unwrap_or_else(|_| ".".to_string())) - .join("data"); + // Use /opt/gbo/data as the base directory + let data_dir = PathBuf::from("/opt/gbo/data"); // Use botserver/work as the work directory for generated files let work_root = PathBuf::from("work"); @@ -50,7 +48,7 @@ impl LocalFileMonitor { } pub async fn start_monitoring(&self) -> Result<(), Box> { - info!("[LOCAL_MONITOR] Starting local file monitor for ~/data/*.gbai directories"); + info!("[LOCAL_MONITOR] Starting local file monitor for /opt/gbo/data/*.gbai directories"); // Create data directory if it doesn't exist if let Err(e) = tokio::fs::create_dir_all(&self.data_dir).await { @@ -157,7 +155,7 @@ impl LocalFileMonitor { } fn is_gbdialog_file(&self, path: &Path) -> bool { - // Check if path is something like ~/data/*.gbai/.gbdialog/*.bas + // Check if path is something like /opt/gbo/data/*.gbai/.gbdialog/*.bas path.extension() .and_then(|e| e.to_str()) .map(|e| e.eq_ignore_ascii_case("bas")) @@ -167,7 +165,7 @@ impl LocalFileMonitor { } async fn scan_and_compile_all(&self) -> Result<(), Box> { - debug!("[LOCAL_MONITOR] Scanning ~/data for .gbai directories"); + debug!("[LOCAL_MONITOR] Scanning /opt/gbo/data for .gbai directories"); let entries = match tokio::fs::read_dir(&self.data_dir).await { Ok(e) => e, @@ -252,7 +250,7 @@ impl LocalFileMonitor { .and_then(|s| s.to_str()) .unwrap_or("unknown"); - // Extract bot name from path like ~/data/cristo.gbai/.gbdialog/file.bas + // Extract bot name from path like /opt/gbo/data/cristo.gbai/.gbdialog/file.bas let bot_name = file_path .ancestors() .find(|p| p.extension().and_then(|e| e.to_str()).map(|e| e.eq_ignore_ascii_case("gbai")).unwrap_or(false)) diff --git a/src/main_module/bootstrap.rs b/src/main_module/bootstrap.rs index a7a85e3e1..ead9cc9fc 100644 --- a/src/main_module/bootstrap.rs +++ b/src/main_module/bootstrap.rs @@ -795,29 +795,28 @@ async fn start_drive_monitors( } }); - // Start local file monitor for ~/data/*.gbai directories + // Start local file monitor for /opt/gbo/data/*.gbai directories let local_monitor_state = app_state.clone(); tokio::spawn(async move { register_thread("local-file-monitor", "drive"); - trace!("Starting LocalFileMonitor for ~/data/*.gbai directories"); + trace!("Starting LocalFileMonitor for /opt/gbo/data/*.gbai directories"); let monitor = crate::drive::local_file_monitor::LocalFileMonitor::new(local_monitor_state); if let Err(e) = monitor.start_monitoring().await { error!("LocalFileMonitor failed: {}", e); } else { - info!("LocalFileMonitor started - watching ~/data/*.gbai/.gbdialog/*.bas"); + info!("LocalFileMonitor started - watching /opt/gbo/data/*.gbai/*.gbdialog/*.bas"); } }); - // Start config file watcher for ~/data/*.gbai/*.gbot/config.csv + // Start config file watcher for /opt/gbo/data/*.gbai/*.gbot/config.csv let config_watcher_state = app_state.clone(); tokio::spawn(async move { register_thread("config-file-watcher", "drive"); - trace!("Starting ConfigWatcher for ~/data/*.gbai/*.gbot/config.csv"); + trace!("Starting ConfigWatcher for /opt/gbo/data/*.gbai/*.gbot/config.csv"); // Determine data directory let data_dir = std::env::var("DATA_DIR") - .or_else(|_| std::env::var("HOME").map(|h| format!("{}/data", h))) - .unwrap_or_else(|_| "./botserver-stack/data".to_string()); + .unwrap_or_else(|_| "/opt/gbo/data".to_string()); let data_dir = std::path::PathBuf::from(data_dir); let watcher = crate::core::config::watcher::ConfigWatcher::new( @@ -826,6 +825,6 @@ async fn start_drive_monitors( ); Arc::new(watcher).spawn(); - info!("ConfigWatcher started - watching ~/data/*.gbai/*.gbot/config.csv"); + info!("ConfigWatcher started - watching /opt/gbo/data/*.gbai/*.gbot/config.csv"); }); }