Skip to main content

ai_agent/utils/plugins/
cache_utils.rs

1// Source: ~/claudecode/openclaudecode/src/utils/plugins/cacheUtils.ts
2#![allow(dead_code)]
3
4use std::collections::HashSet;
5use std::path::{Path, PathBuf};
6use std::sync::OnceLock;
7use std::time::{SystemTime, UNIX_EPOCH};
8
9use tokio::fs;
10
11use super::installed_plugins_manager::load_installed_plugins_from_disk;
12use super::loader::{clear_plugin_cache, get_plugin_cache_path};
13
14const ORPHANED_AT_FILENAME: &str = ".orphaned_at";
15const CLEANUP_AGE_MS: u64 = 7 * 24 * 60 * 60 * 1000; // 7 days
16
17/// Clear all plugin-related caches.
18pub fn clear_all_plugin_caches() {
19    clear_plugin_cache(None);
20    crate::utils::plugins::load_plugin_hooks::clear_plugin_hook_cache();
21    crate::utils::plugins::load_plugin_commands::clear_plugin_command_cache();
22    crate::utils::plugins::load_plugin_agents::clear_plugin_agent_cache();
23    crate::utils::plugins::load_plugin_output_styles::clear_plugin_output_style_cache();
24    crate::utils::plugins::plugin_options_storage::clear_plugin_options_cache();
25}
26
27/// Clear all caches including non-plugin ones.
28pub fn clear_all_caches() {
29    clear_all_plugin_caches();
30}
31
32/// Mark a plugin version as orphaned by writing a .orphaned_at timestamp file.
33pub async fn mark_plugin_version_orphaned(
34    version_path: &Path,
35) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
36    let orphaned_at_path = version_path.join(ORPHANED_AT_FILENAME);
37    let now = SystemTime::now()
38        .duration_since(UNIX_EPOCH)
39        .unwrap_or_default()
40        .as_millis();
41    fs::write(&orphaned_at_path, now.to_string())
42        .await
43        .map_err(|e| {
44            format!(
45                "Failed to write .orphaned_at at {:?}: {}",
46                orphaned_at_path, e
47            )
48        })?;
49    Ok(())
50}
51
52/// Clean up orphaned plugin versions that have been orphaned for more than 7 days.
53pub async fn cleanup_orphaned_plugin_versions_in_background() {
54    if super::zip_cache::is_plugin_zip_cache_enabled() {
55        return;
56    }
57
58    if let Err(e) = do_cleanup().await {
59        log::debug!("Plugin cache cleanup failed: {}", e);
60    }
61}
62
63async fn do_cleanup() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
64    let installed_versions = get_installed_version_paths()?;
65    let cache_path = PathBuf::from(get_plugin_cache_path());
66    let now = SystemTime::now()
67        .duration_since(UNIX_EPOCH)
68        .unwrap_or_default()
69        .as_millis() as u64;
70
71    // Pass 1: Remove .orphaned_at from installed versions
72    for version_path in &installed_versions {
73        remove_orphaned_at_marker(version_path).await;
74    }
75
76    // Pass 2: Process orphaned versions (stub - simplified)
77    log::debug!(
78        "Orphaned cleanup: {} installed versions, cache at {:?}",
79        installed_versions.len(),
80        cache_path
81    );
82    let _ = now; // suppress warning
83    Ok(())
84}
85
86fn get_installed_version_paths()
87-> Result<HashSet<PathBuf>, Box<dyn std::error::Error + Send + Sync>> {
88    let mut paths = HashSet::new();
89    let disk_data = load_installed_plugins_from_disk()?;
90    for installations in disk_data.plugins.values() {
91        for entry in installations {
92            paths.insert(PathBuf::from(&entry.install_path));
93        }
94    }
95    Ok(paths)
96}
97
98async fn remove_orphaned_at_marker(version_path: &Path) {
99    let orphaned_at_path = version_path.join(ORPHANED_AT_FILENAME);
100    if let Err(e) = fs::remove_file(&orphaned_at_path).await {
101        if e.kind() != std::io::ErrorKind::NotFound {
102            log::debug!(
103                "Failed to remove .orphaned_at at {:?}: {}",
104                orphaned_at_path,
105                e
106            );
107        }
108    }
109}
110
111async fn _process_orphaned_plugin_version(_version_path: &Path, _now: u64) {
112    // Stub: simplified implementation
113}
114
115async fn _remove_if_empty(_dir_path: &Path) {
116    // Stub
117}
118
119async fn _read_subdirs(
120    _dir_path: &Path,
121) -> Result<Vec<String>, Box<dyn std::error::Error + Send + Sync>> {
122    Ok(Vec::new())
123}