diff --git a/scripts/dev-api.js b/scripts/dev-api.js
index 6f813de..f1a1884 100644
--- a/scripts/dev-api.js
+++ b/scripts/dev-api.js
@@ -6437,7 +6437,7 @@ const handlers = {
: 'hermes-agent @ git+https://github.com/NousResearch/hermes-agent.git'
const installArgs = method === 'uv-pip'
? ['pip', 'install', pkg]
- : ['tool', 'install', pkg, '--python', '3.11']
+ : ['tool', 'install', '--force', pkg, '--python', '3.11']
const result = spawnSync(uv, installArgs, {
env: { ...process.env, PATH: hermesEnhancedPath(), GIT_TERMINAL_PROMPT: '0' },
timeout: 600000,
diff --git a/src-tauri/src/commands/hermes.rs b/src-tauri/src/commands/hermes.rs
index b9a62d7..ec7d862 100644
--- a/src-tauri/src/commands/hermes.rs
+++ b/src-tauri/src/commands/hermes.rs
@@ -946,7 +946,7 @@ async fn install_via_uv_tool(
};
let mut cmd = tokio::process::Command::new(uv_path);
- cmd.args(["tool", "install", &pkg, "--python", "3.11"]);
+ cmd.args(["tool", "install", "--force", &pkg, "--python", "3.11"]);
// 配置 PyPI 镜像(extras 的依赖仍从 PyPI 下载)
if let Some(mirror) = pypi_mirror_url() {
diff --git a/src/engines/hermes/pages/setup.js b/src/engines/hermes/pages/setup.js
index 80f38b3..883b607 100644
--- a/src/engines/hermes/pages/setup.js
+++ b/src/engines/hermes/pages/setup.js
@@ -33,6 +33,7 @@ export function render() {
let hermesInfo = null
let logs = []
let installing = false
+ let installError = null
let progress = 0
let unlisten = null
@@ -132,35 +133,51 @@ export function render() {
const btnText = installing ? `${ICONS.spinner} ${t('engine.installingBtn')}` : `${ICONS.rocket} ${t('engine.installBtn')}`
const btnDisabled = installing ? 'disabled' : ''
+ // 错误提示块
+ const errorBlock = installError ? `
+
+
+ ${ICONS.error}
+
+
${t('engine.installFailed')}
+
${esc(installError)}
+
+
+
+ ` : ''
+
+ // 进度 + 日志区(安装中或安装失败后都显示)
+ const hasLogs = installing || logs.length > 0
+ const progressBlock = hasLogs ? `
+
+
+
+ ${installError ? t('engine.installFailed') : progress >= 100 ? t('engine.installSuccess') : t('engine.installingBtn')}
+ ${Math.min(progress, 100)}%
+
+
+
+
${logs.map(l => `
${esc(l)}
`).join('')}
+
+ ` : `
+
+
${ICONS.check} ${t('engine.installInfoUv')}
+
${ICONS.check} ${t('engine.installInfoCore')}
+
+
+ ${t('engine.installInfoExtrasLater')}
+
+
+ `
+
return `
${t('engine.installTitle')}
${t('engine.installDescSimple')}
-
- ${installing || progress > 0 ? `
-
-
-
- ${progress >= 100 ? t('engine.installSuccess') : t('engine.installingBtn')}
- ${Math.min(progress, 100)}%
-
-
-
-
${logs.map(l => `
${esc(l)}
`).join('')}
-
- ` : `
-
-
${ICONS.check} ${t('engine.installInfoUv')}
-
${ICONS.check} ${t('engine.installInfoCore')}
-
-
- ${t('engine.installInfoExtrasLater')}
-
-
- `}
-
+ ${errorBlock}
+ ${progressBlock}
-
+
`
@@ -352,6 +369,7 @@ export function render() {
// --- 安装流程 ---
async function doInstall() {
installing = true
+ installError = null
progress = 0
logs = []
draw()
@@ -392,6 +410,7 @@ export function render() {
draw()
} catch (e) {
installing = false
+ installError = String(e.message || e)
logs.push(`❌ ${t('engine.installFailed')}: ${e}`)
draw()
} finally {
diff --git a/src/style/pages.css b/src/style/pages.css
index d866d1e..9e23bad 100644
--- a/src/style/pages.css
+++ b/src/style/pages.css
@@ -1797,6 +1797,9 @@
border-radius: 4px;
transition: width 0.4s ease;
}
+.hermes-progress-bar.error {
+ background: linear-gradient(90deg, var(--error, #ef4444), color-mix(in srgb, var(--error, #ef4444) 80%, #fff));
+}
/* === Hermes Setup — Log Panel === */
.hermes-log-panel {