ai_agent/utils/plugins/
headless_plugin_install.rs1#![allow(dead_code)]
3
4use std::sync::atomic::{AtomicBool, Ordering};
5
6static INSTALLING: AtomicBool = AtomicBool::new(false);
9
10pub async fn install_plugins_for_headless() -> Result<bool, Box<dyn std::error::Error + Send + Sync>>
13{
14 if !INSTALLING
15 .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
16 .is_ok()
17 {
18 return Ok(false);
19 }
20
21 let zip_cache_mode = super::zip_cache::is_plugin_zip_cache_enabled();
22 log::debug!(
23 "install_plugins_for_headless: starting{}",
24 if zip_cache_mode {
25 " (zip cache mode)"
26 } else {
27 ""
28 }
29 );
30
31 let seed_changed = super::marketplace_manager::register_seed_marketplaces().await?;
33 if seed_changed {
34 super::marketplace_manager::clear_marketplaces_cache();
35 super::loader::clear_plugin_cache(Some(
36 "headless_plugin_install: seed marketplaces registered",
37 ));
38 }
39
40 if zip_cache_mode {
42 log::debug!("Zip cache mode: would create directory structure");
44 }
45
46 let declared_count = super::marketplace_manager::get_declared_marketplaces().len();
47 let mut metrics = HeadlessInstallMetrics::default();
48 let mut plugins_changed = seed_changed;
49
50 let result = (async {
51 if declared_count == 0 {
52 log::debug!("install_plugins_for_headless: no marketplaces declared");
53 } else {
54 let reconcile_result = super::reconciler::reconcile_marketplaces(
55 Some(super::reconciler::ReconcileOptions {
56 skip: if zip_cache_mode {
57 Some(Box::new(|_name: &str, source: &super::schemas::MarketplaceSource| {
58 !super::zip_cache::is_marketplace_source_supported_by_zip_cache(source)
59 }))
60 } else {
61 None
62 },
63 on_progress: Some(Box::new(|event: super::reconciler::ReconcileProgressEvent| {
64 match event {
65 super::reconciler::ReconcileProgressEvent::Installed { name, .. } => {
66 log::debug!("install_plugins_for_headless: installed marketplace {}", name);
67 }
68 super::reconciler::ReconcileProgressEvent::Failed { name, error } => {
69 log::debug!("install_plugins_for_headless: failed to install marketplace {}: {}", name, error);
70 }
71 _ => {}
72 }
73 })),
74 }),
75 )
76 .await?;
77
78 let marketplaces_changed = reconcile_result.installed.len() + reconcile_result.updated.len();
79 if marketplaces_changed > 0 {
80 super::marketplace_manager::clear_marketplaces_cache();
81 super::loader::clear_plugin_cache(Some("headless_plugin_install: marketplaces reconciled"));
82 plugins_changed = true;
83 }
84 metrics.marketplaces_installed = marketplaces_changed;
85 }
86
87 if zip_cache_mode {
89 super::zip_cache_adapters::sync_marketplaces_to_zip_cache().await?;
90 }
91
92 let newly_delisted = super::plugin_blocklist::detect_and_uninstall_delisted_plugins().await?;
94 metrics.delisted_count = newly_delisted.len();
95 if !newly_delisted.is_empty() {
96 plugins_changed = true;
97 }
98
99 if plugins_changed {
100 super::loader::clear_plugin_cache(Some("headless_plugin_install: plugins changed"));
101 }
102
103 if zip_cache_mode {
105 log::debug!("Zip cache mode: would register session cleanup");
107 }
108
109 Ok(plugins_changed)
110 })
111 .await;
112
113 INSTALLING.store(false, Ordering::SeqCst);
114
115 result
122}
123
124#[derive(Default)]
125struct HeadlessInstallMetrics {
126 marketplaces_installed: usize,
127 delisted_count: usize,
128}