修复Agent安装/升级在特殊环境下会失败
This commit is contained in:
@@ -120,11 +120,7 @@ async fn detect_local_version(agent_type: AgentType) -> Option<String> {
|
|||||||
}
|
}
|
||||||
// Try `npm list -g <package_name> --json` to get the real installed version.
|
// Try `npm list -g <package_name> --json` to get the real installed version.
|
||||||
let pkg_name = package_name_from_spec(package);
|
let pkg_name = package_name_from_spec(package);
|
||||||
if let Some(v) = detect_npm_global_version(&pkg_name).await {
|
detect_npm_global_version(&pkg_name).await
|
||||||
return Some(v);
|
|
||||||
}
|
|
||||||
// Fallback: parse version from registry package spec
|
|
||||||
version_from_package_spec(package)
|
|
||||||
}
|
}
|
||||||
registry::AgentDistribution::Binary { cmd, .. } => {
|
registry::AgentDistribution::Binary { cmd, .. } => {
|
||||||
binary_cache::detect_installed_version(agent_type, cmd)
|
binary_cache::detect_installed_version(agent_type, cmd)
|
||||||
@@ -134,17 +130,48 @@ async fn detect_local_version(agent_type: AgentType) -> Option<String> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Official npm registry URL – used to bypass local mirror configurations that
|
||||||
|
/// may not have synced niche packages like `@agentclientprotocol/*`.
|
||||||
|
const NPM_OFFICIAL_REGISTRY: &str = "https://registry.npmjs.org";
|
||||||
|
|
||||||
async fn install_npm_global_package(package: &str) -> Result<(), AcpError> {
|
async fn install_npm_global_package(package: &str) -> Result<(), AcpError> {
|
||||||
|
let registry_arg = format!("--registry={NPM_OFFICIAL_REGISTRY}");
|
||||||
|
|
||||||
let output = crate::process::tokio_command("npm")
|
let output = crate::process::tokio_command("npm")
|
||||||
.arg("install")
|
.arg("install")
|
||||||
.arg("-g")
|
.arg("-g")
|
||||||
|
.arg(®istry_arg)
|
||||||
.arg(package)
|
.arg(package)
|
||||||
.output()
|
.output()
|
||||||
.await
|
.await
|
||||||
.map_err(|e| AcpError::protocol(format!("failed to run npm install -g: {e}")))?;
|
.map_err(|e| AcpError::protocol(format!("failed to run npm install -g: {e}")))?;
|
||||||
|
|
||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
let err = String::from_utf8_lossy(&output.stderr).trim().to_string();
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||||
|
// EEXIST: file conflict — retry with --force to overwrite
|
||||||
|
if stderr.contains("EEXIST") {
|
||||||
|
let retry = crate::process::tokio_command("npm")
|
||||||
|
.arg("install")
|
||||||
|
.arg("-g")
|
||||||
|
.arg("--force")
|
||||||
|
.arg(®istry_arg)
|
||||||
|
.arg(package)
|
||||||
|
.output()
|
||||||
|
.await
|
||||||
|
.map_err(|e| AcpError::protocol(format!("failed to run npm install -g --force: {e}")))?;
|
||||||
|
if !retry.status.success() {
|
||||||
|
let err = String::from_utf8_lossy(&retry.stderr).trim().to_string();
|
||||||
|
let msg = if err.is_empty() {
|
||||||
|
"failed to install npm package globally (with --force)".to_string()
|
||||||
|
} else {
|
||||||
|
format!("failed to install npm package globally (with --force): {err}")
|
||||||
|
};
|
||||||
|
return Err(AcpError::protocol(msg));
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let err = stderr.trim().to_string();
|
||||||
let msg = if err.is_empty() {
|
let msg = if err.is_empty() {
|
||||||
"failed to install npm package globally".to_string()
|
"failed to install npm package globally".to_string()
|
||||||
} else {
|
} else {
|
||||||
@@ -1228,11 +1255,13 @@ pub async fn acp_list_agents(
|
|||||||
let setting = settings_map.get(&agent_type);
|
let setting = settings_map.get(&agent_type);
|
||||||
let meta = registry::get_agent_meta(agent_type);
|
let meta = registry::get_agent_meta(agent_type);
|
||||||
let (available, dist_type, local_installed_version) = match &meta.distribution {
|
let (available, dist_type, local_installed_version) = match &meta.distribution {
|
||||||
registry::AgentDistribution::Npx { .. } => (
|
registry::AgentDistribution::Npx { .. } => {
|
||||||
true,
|
// Detect NPX agent version dynamically, fall back to DB value
|
||||||
"npx",
|
let detected = detect_local_version(agent_type).await.or_else(|| {
|
||||||
setting.and_then(|m| m.installed_version.clone()),
|
setting.and_then(|m| m.installed_version.clone())
|
||||||
),
|
});
|
||||||
|
(true, "npx", detected)
|
||||||
|
}
|
||||||
registry::AgentDistribution::Binary { platforms, cmd, .. } => {
|
registry::AgentDistribution::Binary { platforms, cmd, .. } => {
|
||||||
let detected = binary_cache::detect_installed_version(agent_type, cmd)
|
let detected = binary_cache::detect_installed_version(agent_type, cmd)
|
||||||
.ok()
|
.ok()
|
||||||
@@ -1274,14 +1303,13 @@ pub async fn acp_list_agents(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let sort_order = setting.map(|m| m.sort_order).unwrap_or(idx as i32);
|
let sort_order = setting.map(|m| m.sort_order).unwrap_or(idx as i32);
|
||||||
if dist_type == "binary" {
|
// Persist detected version to DB for both binary and npx agents
|
||||||
let _ = agent_setting_service::set_installed_version(
|
let _ = agent_setting_service::set_installed_version(
|
||||||
&db.conn,
|
&db.conn,
|
||||||
agent_type,
|
agent_type,
|
||||||
local_installed_version.clone(),
|
local_installed_version.clone(),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
}
|
|
||||||
let codex_auth_json = if agent_type == AgentType::Codex {
|
let codex_auth_json = if agent_type == AgentType::Codex {
|
||||||
load_codex_auth_json_raw()
|
load_codex_auth_json_raw()
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user