fix(drivers): skip blocked driver updates

This commit is contained in:
t8y2 2026-06-03 14:39:46 +08:00
parent 381bdfdfd7
commit 7fb53ee0b4
14 changed files with 299 additions and 15 deletions

View File

@ -212,6 +212,11 @@ async function runDriverInstall(dbType: string) {
installing.value = dbType;
progress.value = null;
try {
const blockers = await api.checkAgentUpdateBlockers([dbType]);
if (blockers.length > 0) {
toast(t("driverStore.driverUpdateBlocked", { labels: blockers.map((blocker) => blocker.label).join(", ") }));
return;
}
await api.installAgent(dbType);
await refreshAgents();
toast(t("driverStore.driverInstallSuccess", { label }));
@ -239,9 +244,22 @@ async function upgradeAll() {
queuedDriverInstalls.value = [];
progress.value = null;
try {
const count = await api.upgradeAllAgents();
const updatableDbTypes = drivers.value.filter((driver) => driver.update_available).map((driver) => driver.db_type);
const blockers = await api.checkAgentUpdateBlockers(updatableDbTypes);
if (blockers.length > 0) {
toast(t("driverStore.driverUpdateBlocked", { labels: blockers.map((blocker) => blocker.label).join(", ") }));
return;
}
const result = await api.upgradeAllAgents();
await refreshAgents();
toast(t("driverStore.upgradeAllSuccess", { count }));
if (result.failed.length > 0) {
const failedLabels = result.failed
.map((item) => drivers.value.find((driver) => driver.db_type === item.db_type)?.label ?? item.db_type)
.join(", ");
toast(t("driverStore.upgradeAllPartial", { count: result.upgraded, failed: failedLabels }));
} else {
toast(t("driverStore.upgradeAllSuccess", { count: result.upgraded }));
}
} catch (e: any) {
toast(t("driverStore.upgradeAllFailed", { error: e }));
} finally {

View File

@ -1764,7 +1764,9 @@ export default {
chooseJavaExecutable: "Choose Java executable",
driverInstallSuccess: "{label} driver installed",
driverInstallFailed: "Failed to install {label} driver: {error}",
driverUpdateBlocked: "Close these database connections before updating drivers: {labels}",
upgradeAllSuccess: "{count} driver(s) upgraded",
upgradeAllPartial: "{count} driver(s) upgraded. Skipped: {failed}",
upgradeAllFailed: "Batch upgrade failed: {error}",
driverUninstallSuccess: "{label} driver uninstalled",
driverUninstallFailed: "Failed to uninstall {label} driver: {error}",

View File

@ -1655,7 +1655,9 @@ export default {
chooseJavaExecutable: "Seleccionar ejecutable Java",
driverInstallSuccess: "Driver {label} instalado",
driverInstallFailed: "Error al instalar el driver {label}: {error}",
driverUpdateBlocked: "Cierra estas conexiones de base de datos antes de actualizar drivers: {labels}",
upgradeAllSuccess: "{count} driver(s) actualizados",
upgradeAllPartial: "{count} driver(s) actualizados. Omitidos: {failed}",
upgradeAllFailed: "Error al actualizar en lote: {error}",
driverUninstallSuccess: "Driver {label} desinstalado",
driverUninstallFailed: "Error al desinstalar el driver {label}: {error}",

View File

@ -1725,7 +1725,9 @@ export default {
chooseJavaExecutable: "选择 Java 可执行文件",
driverInstallSuccess: "{label} 驱动安装成功",
driverInstallFailed: "{label} 驱动安装失败: {error}",
driverUpdateBlocked: "请先关闭以下数据库连接后再更新驱动: {labels}",
upgradeAllSuccess: "{count} 个驱动升级完成",
upgradeAllPartial: "{count} 个驱动升级完成,以下驱动已跳过: {failed}",
upgradeAllFailed: "批量升级失败: {error}",
driverUninstallSuccess: "{label} 驱动已卸载",
driverUninstallFailed: "{label} 驱动卸载失败: {error}",

View File

@ -1691,7 +1691,9 @@ export default {
chooseJavaExecutable: "選擇 Java 可執行檔",
driverInstallSuccess: "{label} 驅動程式安裝成功",
driverInstallFailed: "{label} 驅動程式安裝失敗: {error}",
driverUpdateBlocked: "請先關閉以下資料庫連線後再更新驅動程式: {labels}",
upgradeAllSuccess: "{count} 個驅動程式更新完成",
upgradeAllPartial: "{count} 個驅動程式更新完成,以下驅動程式已略過: {failed}",
upgradeAllFailed: "批次更新失敗: {error}",
driverUninstallSuccess: "{label} 驅動程式已解除安裝",
driverUninstallFailed: "{label} 驅動程式解除安裝失敗: {error}",

View File

@ -51,6 +51,7 @@ export const listInstalledAgents = forward("listInstalledAgents");
export const getDriverStoreUsage = forward("getDriverStoreUsage");
export const installAgent = forward("installAgent");
export const upgradeAllAgents = forward("upgradeAllAgents");
export const checkAgentUpdateBlockers = forward("checkAgentUpdateBlockers");
export const uninstallAgent = forward("uninstallAgent");
export const getAgentJavaRuntimeConfig = forward("getAgentJavaRuntimeConfig");
export const setAgentJavaRuntimeConfig = forward("setAgentJavaRuntimeConfig");

View File

@ -28,6 +28,8 @@ import type {
AiConversation,
AiModelInfo,
DriverStoreUsage,
UpgradeAllAgentDriversResult,
AgentUpdateBlocker,
DesktopSettings,
DriverInstallProgress,
JavaRuntimeConfig,
@ -242,9 +244,12 @@ export async function installAgent(dbType: string): Promise<void> {
await post("/api/agents/install", { dbType });
}
export async function upgradeAllAgents(): Promise<number> {
const result: { count: number } = await post("/api/agents/upgrade-all", {});
return result.count;
export async function upgradeAllAgents(): Promise<UpgradeAllAgentDriversResult> {
return post("/api/agents/upgrade-all", {});
}
export async function checkAgentUpdateBlockers(_dbTypes: string[]): Promise<AgentUpdateBlocker[]> {
return [];
}
export async function uninstallAgent(dbType: string): Promise<void> {

View File

@ -74,6 +74,21 @@ export interface AgentDriverInfo {
jre_installed: boolean;
}
export interface AgentDriverUpdateIssue {
db_type: string;
error: string;
}
export interface UpgradeAllAgentDriversResult {
upgraded: number;
failed: AgentDriverUpdateIssue[];
}
export interface AgentUpdateBlocker {
db_type: string;
label: string;
}
export type JavaRuntimeMode = "managed" | "system" | "custom";
export interface JavaRuntimeConfig {
@ -90,6 +105,7 @@ export interface DriverStoreUsage {
total_bytes: number;
jre_bytes: number;
agent_driver_bytes: number;
download_cache_bytes?: number;
jdbc_plugin_bytes: number;
jdbc_driver_bytes: number;
jres: DriverStoreUsageItem[];
@ -847,10 +863,14 @@ export async function installAgent(dbType: string): Promise<void> {
return invoke("install_agent", { dbType });
}
export async function upgradeAllAgents(): Promise<number> {
export async function upgradeAllAgents(): Promise<UpgradeAllAgentDriversResult> {
return invoke("upgrade_all_agents");
}
export async function checkAgentUpdateBlockers(dbTypes: string[]): Promise<AgentUpdateBlocker[]> {
return invoke("check_agent_update_blockers", { dbTypes });
}
export async function uninstallAgent(dbType: string): Promise<void> {
return invoke("uninstall_agent", { dbType });
}

View File

@ -8,6 +8,8 @@ use crate::db::agent_driver::{AgentDriverClient, AgentMethod};
use crate::models::connection::DatabaseType;
pub const DEFAULT_JRE_KEY: &str = "21";
pub const DOWNLOAD_CACHE_DIR_NAME: &str = "download-cache";
pub const DOWNLOAD_CACHE_MAX_AGE_DAYS: u64 = 7;
fn default_jre_key() -> String {
DEFAULT_JRE_KEY.to_string()
@ -245,6 +247,8 @@ pub struct DriverStoreUsage {
pub total_bytes: u64,
pub jre_bytes: u64,
pub agent_driver_bytes: u64,
#[serde(default)]
pub download_cache_bytes: u64,
pub jdbc_plugin_bytes: u64,
pub jdbc_driver_bytes: u64,
pub jres: Vec<DriverStoreUsageItem>,
@ -320,6 +324,14 @@ impl AgentManager {
self.base_dir.join("drivers").join(db_type).join("agent.jar")
}
pub fn download_cache_dir(&self) -> PathBuf {
self.base_dir.join(DOWNLOAD_CACHE_DIR_NAME)
}
pub fn download_cache_max_age_days(&self) -> u64 {
DOWNLOAD_CACHE_MAX_AGE_DAYS
}
fn state_path(&self) -> PathBuf {
self.base_dir.join("state.json")
}
@ -390,14 +402,17 @@ impl AgentManager {
let jdbc_driver_bytes = path_size_bytes(&jdbc_driver_root);
let jdbc_total_bytes = path_size_bytes(&jdbc_root);
let jdbc_plugin_bytes = jdbc_total_bytes.saturating_sub(jdbc_driver_bytes);
let download_cache_bytes = path_size_bytes(&self.download_cache_dir());
DriverStoreUsage {
total_bytes: jre_bytes
.saturating_add(agent_driver_bytes)
.saturating_add(download_cache_bytes)
.saturating_add(jdbc_plugin_bytes)
.saturating_add(jdbc_driver_bytes),
jre_bytes,
agent_driver_bytes,
download_cache_bytes,
jdbc_plugin_bytes,
jdbc_driver_bytes,
jres,
@ -440,6 +455,10 @@ impl AgentManager {
crate::agent_runtime::stop_daemon_by_key(self, agent_key).await;
}
pub async fn active_daemon_keys(&self) -> Vec<String> {
self.daemons.lock().await.keys().cloned().collect()
}
pub fn db_type_to_agent_key(db_type: &DatabaseType, driver_profile: Option<&str>) -> Option<&'static str> {
crate::agent_runtime::db_type_to_agent_key(db_type, driver_profile)
}

View File

@ -1,3 +1,4 @@
use std::hash::{Hash, Hasher};
use std::io::Read;
use std::path::{Path, PathBuf};
@ -27,6 +28,18 @@ pub struct AgentProgressEvent {
pub total_drivers: Option<u32>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
pub struct AgentDriverUpdateIssue {
pub db_type: String,
pub error: String,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq, Default)]
pub struct UpgradeAllAgentDriversResult {
pub upgraded: u32,
pub failed: Vec<AgentDriverUpdateIssue>,
}
impl AgentProgressEvent {
pub fn step(step: impl Into<String>) -> Self {
Self { step: step.into(), downloaded: None, total: None, db_type: None, current: None, total_drivers: None }
@ -163,14 +176,15 @@ pub async fn install_agent_driver(
pub async fn upgrade_all_agent_drivers(
am: &AgentManager,
progress: impl Fn(AgentProgressEvent),
) -> Result<u32, String> {
) -> Result<UpgradeAllAgentDriversResult, String> {
let registry = fetch_registry().await?;
let agents = build_agent_list(am, Some(&registry));
let updatable: Vec<&AgentDriverInfo> = agents.iter().filter(|agent| agent.update_available).collect();
let total = updatable.len() as u32;
let mut result = UpgradeAllAgentDriversResult::default();
for (index, agent) in updatable.iter().enumerate() {
install_agent_driver_from_registry(
match install_agent_driver_from_registry(
am,
&registry,
&agent.db_type,
@ -178,11 +192,18 @@ pub async fn upgrade_all_agent_drivers(
Some((index + 1) as u32),
Some(total),
)
.await?;
.await
{
Ok(()) => result.upgraded += 1,
Err(error) => {
log::warn!("Failed to update {} agent driver: {}", agent.db_type, error);
result.failed.push(AgentDriverUpdateIssue { db_type: agent.db_type.clone(), error });
}
}
}
progress(AgentProgressEvent::step("all-done"));
Ok(total)
Ok(result)
}
pub async fn uninstall_agent_driver(am: &AgentManager, db_type: &str) -> Result<(), String> {
@ -238,6 +259,7 @@ pub async fn reinstall_agent_jre(
.ok_or_else(|| format!("No JRE {jre_key} available for platform: {platform}"))?;
let jre_archive = am.base_dir().join("jre-download.tar.gz");
download_with_progress(
am,
&progress,
"jre",
&platform_jre.url,
@ -338,6 +360,7 @@ async fn install_agent_driver_from_registry(
total_drivers,
));
download_with_progress(
am,
progress,
"jre",
&platform_jre.url,
@ -365,6 +388,7 @@ async fn install_agent_driver_from_registry(
total_drivers,
));
download_with_progress(
am,
progress,
"driver",
&driver.jar.url,
@ -396,6 +420,7 @@ async fn install_agent_driver_from_registry(
}
async fn download_with_progress(
am: &AgentManager,
progress: &impl Fn(AgentProgressEvent),
step: &str,
url: &str,
@ -410,6 +435,18 @@ async fn download_with_progress(
std::fs::create_dir_all(parent).map_err(|err| err.to_string())?;
}
let tmp = download_temp_path(dest);
let cache_path = cached_download_path(am, url, total_size, dest);
prune_download_cache(am).ok();
if cached_download_is_valid(am, &cache_path, total_size) {
std::fs::copy(&cache_path, &tmp).map_err(|err| format!("Failed to copy cached download: {err}"))?;
progress(AgentProgressEvent::transfer(step, total_size, total_size).with_batch(
db_type,
current,
total_drivers,
));
return replace_download(&tmp, dest);
}
let client = reqwest::Client::builder()
.timeout(std::time::Duration::from_secs(300))
.build()
@ -431,9 +468,62 @@ async fn download_with_progress(
}
std::io::Write::flush(&mut file).map_err(|err| format!("Failed to flush temp file: {err}"))?;
drop(file);
if let Some(parent) = cache_path.parent() {
if let Err(err) = std::fs::create_dir_all(parent) {
log::warn!("Failed to create agent download cache directory: {err}");
} else if let Err(err) = std::fs::copy(&tmp, &cache_path) {
log::warn!("Failed to cache agent download: {err}");
}
}
replace_download(&tmp, dest)
}
fn cached_download_path(am: &AgentManager, url: &str, total_size: u64, dest: &std::path::Path) -> std::path::PathBuf {
let mut hasher = std::collections::hash_map::DefaultHasher::new();
url.hash(&mut hasher);
total_size.hash(&mut hasher);
let hash = hasher.finish();
let file_name = dest.file_name().and_then(|name| name.to_str()).unwrap_or("download");
am.download_cache_dir().join(format!("{hash:016x}-{file_name}"))
}
fn cached_download_is_valid(am: &AgentManager, path: &std::path::Path, expected_size: u64) -> bool {
let Ok(meta) = std::fs::metadata(path) else {
return false;
};
if !meta.is_file() {
return false;
}
if expected_size > 0 && meta.len() != expected_size {
let _ = std::fs::remove_file(path);
return false;
}
let max_age = std::time::Duration::from_secs(am.download_cache_max_age_days() * 24 * 60 * 60);
if meta.modified().ok().and_then(|modified| modified.elapsed().ok()).is_some_and(|age| age > max_age) {
let _ = std::fs::remove_file(path);
return false;
}
true
}
fn prune_download_cache(am: &AgentManager) -> Result<(), String> {
let cache_dir = am.download_cache_dir();
let max_age = std::time::Duration::from_secs(am.download_cache_max_age_days() * 24 * 60 * 60);
let Ok(entries) = std::fs::read_dir(&cache_dir) else {
return Ok(());
};
for entry in entries.flatten() {
let path = entry.path();
let Ok(meta) = entry.metadata() else {
continue;
};
if meta.modified().ok().and_then(|modified| modified.elapsed().ok()).is_some_and(|age| age > max_age) {
let _ = if meta.is_dir() { std::fs::remove_dir_all(path) } else { std::fs::remove_file(path) };
}
}
Ok(())
}
pub fn github_url_to_r2_path(github_url: &str, category: &str) -> String {
let filename = github_url.rsplit('/').next().unwrap_or(github_url);
match category {

View File

@ -1,4 +1,4 @@
use std::collections::HashMap;
use std::collections::{HashMap, HashSet};
use std::path::PathBuf;
use std::sync::Arc;
use tokio::sync::RwLock;
@ -650,6 +650,36 @@ impl AppState {
Ok(closed)
}
pub async fn active_agent_driver_keys(&self) -> HashSet<String> {
let configs = self.configs.read().await;
let connections = self.connections.read().await;
let mut keys = HashSet::new();
for (pool_key, pool) in connections.iter() {
if !matches!(pool, PoolKind::Agent(_)) {
continue;
}
let Some(config) = config_for_pool_key(pool_key, &configs) else {
continue;
};
if let Some(agent_key) = crate::agent_manager::AgentManager::db_type_to_agent_key(
&config.db_type,
config.driver_profile.as_deref(),
) {
keys.insert(agent_key.to_string());
}
}
drop(connections);
drop(configs);
for key in self.agent_manager.active_daemon_keys().await {
keys.insert(key);
}
keys
}
pub async fn duckdb_existing_pool_is_usable_for_config(&self, config: &ConnectionConfig) -> Result<bool, String> {
if config.db_type != DatabaseType::DuckDb {
return Ok(false);
@ -800,6 +830,19 @@ fn session_scoped_pool_key(base_pool_key: String, client_session_id: Option<&str
.unwrap_or(base_pool_key)
}
fn config_for_pool_key<'a>(
pool_key: &str,
configs: &'a HashMap<String, ConnectionConfig>,
) -> Option<&'a ConnectionConfig> {
configs
.iter()
.filter(|(connection_id, _)| {
pool_key.strip_prefix(connection_id.as_str()).is_some_and(|rest| rest.is_empty() || rest.starts_with(':'))
})
.max_by_key(|(connection_id, _)| connection_id.len())
.map(|(_, config)| config)
}
fn session_scoped_pool_key_for(
db_type: Option<DatabaseType>,
base_pool_key: String,

View File

@ -55,6 +55,7 @@ pub async fn install_agent(
State(state): State<Arc<WebState>>,
Json(req): Json<AgentTypeRequest>,
) -> Result<Json<serde_json::Value>, AppError> {
ensure_no_agent_update_blockers(&state.app, std::slice::from_ref(&req.db_type)).await.map_err(AppError)?;
let tx = progress_sender(&state, "global").await;
install_agent_driver(&state.app.agent_manager, &req.db_type, |event| send_progress_event(&tx, event))
.await
@ -63,11 +64,16 @@ pub async fn install_agent(
}
pub async fn upgrade_all_agents(State(state): State<Arc<WebState>>) -> Result<Json<serde_json::Value>, AppError> {
let registry = fetch_registry().await.map_err(AppError)?;
let agents = build_agent_list(&state.app.agent_manager, Some(&registry));
let updatable: Vec<String> =
agents.iter().filter(|agent| agent.update_available).map(|agent| agent.db_type.clone()).collect();
ensure_no_agent_update_blockers(&state.app, &updatable).await.map_err(AppError)?;
let tx = progress_sender(&state, "global").await;
let total = upgrade_all_agent_drivers(&state.app.agent_manager, |event| send_progress_event(&tx, event))
let result = upgrade_all_agent_drivers(&state.app.agent_manager, |event| send_progress_event(&tx, event))
.await
.map_err(AppError)?;
Ok(Json(serde_json::json!({ "count": total })))
Ok(Json(serde_json::to_value(result).map_err(|err| AppError(err.to_string()))?))
}
pub async fn uninstall_agent(
@ -224,3 +230,26 @@ fn send_progress_event(tx: &broadcast::Sender<String>, event: AgentProgressEvent
let _ = tx.send(payload);
}
}
async fn ensure_no_agent_update_blockers(
state: &dbx_core::connection::AppState,
db_types: &[String],
) -> Result<(), String> {
let candidate_keys: std::collections::HashSet<&str> = db_types.iter().map(String::as_str).collect();
if candidate_keys.is_empty() {
return Ok(());
}
let mut blockers = state
.active_agent_driver_keys()
.await
.into_iter()
.filter(|key| candidate_keys.contains(key.as_str()))
.map(|key| dbx_core::agent_catalog::label_for_key(&key).unwrap_or(&key).to_string())
.collect::<Vec<_>>();
blockers.sort();
if blockers.is_empty() {
Ok(())
} else {
Err(format!("请先关闭以下数据库连接后再更新驱动: {}", blockers.join(", ")))
}
}

View File

@ -6,10 +6,16 @@ use dbx_core::agent_manager::{AgentDriverInfo, DriverStoreUsage, JavaRuntimeConf
use dbx_core::agent_service::{
build_agent_list, fetch_registry, import_agent_jar, import_agents_from_zip as import_agents_from_zip_core,
install_agent_driver, invalidate_registry_cache, reinstall_agent_jre, uninstall_agent_driver, uninstall_agent_jre,
upgrade_all_agent_drivers, AgentProgressEvent,
upgrade_all_agent_drivers, AgentProgressEvent, UpgradeAllAgentDriversResult,
};
use dbx_core::connection::AppState;
#[derive(Debug, Clone, serde::Serialize)]
pub struct AgentUpdateBlocker {
pub db_type: String,
pub label: String,
}
#[tauri::command]
pub async fn list_installed_agents_local(state: State<'_, Arc<AppState>>) -> Result<Vec<AgentDriverInfo>, String> {
Ok(build_agent_list(&state.agent_manager, None))
@ -32,16 +38,33 @@ pub async fn install_agent(
state: State<'_, Arc<AppState>>,
db_type: String,
) -> Result<(), String> {
ensure_no_agent_update_blockers(state.inner().as_ref(), std::slice::from_ref(&db_type)).await?;
let app_handle = app.clone();
install_agent_driver(&state.agent_manager, &db_type, move |event| emit_agent_progress(&app_handle, event)).await
}
#[tauri::command]
pub async fn upgrade_all_agents(app: tauri::AppHandle, state: State<'_, Arc<AppState>>) -> Result<u32, String> {
pub async fn upgrade_all_agents(
app: tauri::AppHandle,
state: State<'_, Arc<AppState>>,
) -> Result<UpgradeAllAgentDriversResult, String> {
let registry = fetch_registry().await?;
let agents = build_agent_list(&state.agent_manager, Some(&registry));
let updatable: Vec<String> =
agents.iter().filter(|agent| agent.update_available).map(|agent| agent.db_type.clone()).collect();
ensure_no_agent_update_blockers(state.inner().as_ref(), &updatable).await?;
let app_handle = app.clone();
upgrade_all_agent_drivers(&state.agent_manager, move |event| emit_agent_progress(&app_handle, event)).await
}
#[tauri::command]
pub async fn check_agent_update_blockers(
state: State<'_, Arc<AppState>>,
db_types: Vec<String>,
) -> Result<Vec<AgentUpdateBlocker>, String> {
Ok(agent_update_blockers(state.inner().as_ref(), &db_types).await)
}
#[tauri::command]
pub async fn uninstall_agent(state: State<'_, Arc<AppState>>, db_type: String) -> Result<(), String> {
uninstall_agent_driver(&state.agent_manager, &db_type).await
@ -131,3 +154,30 @@ pub async fn reinstall_jre(
fn emit_agent_progress(app: &tauri::AppHandle, event: AgentProgressEvent) {
let _ = app.emit("agent-install-progress", event);
}
async fn ensure_no_agent_update_blockers(state: &AppState, db_types: &[String]) -> Result<(), String> {
let blockers = agent_update_blockers(state, db_types).await;
if blockers.is_empty() {
return Ok(());
}
let labels = blockers.into_iter().map(|blocker| blocker.label).collect::<Vec<_>>().join(", ");
Err(format!("请先关闭以下数据库连接后再更新驱动: {labels}"))
}
async fn agent_update_blockers(state: &AppState, db_types: &[String]) -> Vec<AgentUpdateBlocker> {
let candidate_keys: std::collections::HashSet<&str> = db_types.iter().map(String::as_str).collect();
if candidate_keys.is_empty() {
return Vec::new();
}
let active_keys = state.active_agent_driver_keys().await;
let mut blockers = active_keys
.into_iter()
.filter(|key| candidate_keys.contains(key.as_str()))
.map(|db_type| AgentUpdateBlocker {
label: dbx_core::agent_catalog::label_for_key(&db_type).unwrap_or(&db_type).to_string(),
db_type,
})
.collect::<Vec<_>>();
blockers.sort_by(|left, right| left.label.cmp(&right.label));
blockers
}

View File

@ -512,6 +512,7 @@ pub fn run() {
commands::agents::get_driver_store_usage,
commands::agents::install_agent,
commands::agents::upgrade_all_agents,
commands::agents::check_agent_update_blockers,
commands::agents::uninstall_agent,
commands::agents::check_jre_installed,
commands::agents::get_agent_java_runtime_config,