fix false missing Hermes CLI detection after deployment
This commit is contained in:
@@ -144,30 +144,38 @@ export async function checkHealth(): Promise<HealthResponse> {
|
||||
export interface HermesCliStatus {
|
||||
available: boolean
|
||||
bin?: string
|
||||
reason?: 'ready' | 'missing' | 'permission_denied' | 'timeout' | 'failed'
|
||||
}
|
||||
|
||||
let cliProbePromise: Promise<boolean> | null = null
|
||||
let cliProbePromise: Promise<HermesCliStatus> | null = null
|
||||
let refreshCliProbe = false
|
||||
|
||||
async function probeHermesCli(): Promise<boolean> {
|
||||
async function probeHermesCli(refresh: boolean): Promise<HermesCliStatus> {
|
||||
try {
|
||||
const res = await request<HermesCliStatus>('/api/hermes/cli-status')
|
||||
return res.available !== false
|
||||
return await request<HermesCliStatus>(`/api/hermes/cli-status${refresh ? '?refresh=1' : ''}`)
|
||||
} catch {
|
||||
// Fail open: probe errors (older servers, mocked backends) must not block the app.
|
||||
return true
|
||||
// Probe transport errors must not block access to the rest of the application.
|
||||
return { available: true }
|
||||
}
|
||||
}
|
||||
|
||||
/** Cached per page load; the router guard probes once before showing the app. */
|
||||
export function checkHermesCliAvailable(): Promise<boolean> {
|
||||
export function getHermesCliStatus(): Promise<HermesCliStatus> {
|
||||
if (!cliProbePromise) {
|
||||
cliProbePromise = probeHermesCli()
|
||||
cliProbePromise = probeHermesCli(refreshCliProbe)
|
||||
refreshCliProbe = false
|
||||
}
|
||||
return cliProbePromise
|
||||
}
|
||||
|
||||
/** A slow startup is not evidence that Hermes needs installing. */
|
||||
export async function checkHermesCliAvailable(): Promise<boolean> {
|
||||
const status = await getHermesCliStatus()
|
||||
return status.available !== false || status.reason === 'timeout'
|
||||
}
|
||||
|
||||
export function resetHermesCliProbe(): void {
|
||||
cliProbePromise = null
|
||||
refreshCliProbe = true
|
||||
}
|
||||
|
||||
export async function triggerUpdate(): Promise<{ success: boolean; message: string }> {
|
||||
|
||||
@@ -53,6 +53,12 @@ export default {
|
||||
|
||||
// hermes CLI missing guide
|
||||
hermesMissing: {
|
||||
repairHint: 'After fixing the executable or runtime, use Re-check below.',
|
||||
permissionTitle: 'Hermes CLI cannot be executed',
|
||||
permissionIntro: 'The service cannot execute Hermes or its Python interpreter. Check executable permissions and directory access for the service user.',
|
||||
failedTitle: 'Hermes CLI failed to start',
|
||||
failedIntro: 'Hermes was found but its startup check failed. Check the service logs and the Hermes Python environment.',
|
||||
|
||||
title: 'Hermes CLI not detected',
|
||||
intro: 'hermes-ui no longer bundles the hermes CLI. Install it on the machine running this service first:',
|
||||
installCmd: 'pip install hermes-agent',
|
||||
|
||||
@@ -53,6 +53,12 @@ export default {
|
||||
|
||||
// hermes CLI 缺失引导
|
||||
hermesMissing: {
|
||||
repairHint: '修复可执行文件或运行环境后,点击下方按钮重新检测。',
|
||||
permissionTitle: 'Hermes CLI 执行权限不足',
|
||||
permissionIntro: '服务无法执行 Hermes 或其 Python 解释器,请检查服务用户的文件执行权限和目录访问权限。',
|
||||
failedTitle: 'Hermes CLI 启动失败',
|
||||
failedIntro: '已找到 Hermes,但启动检测失败。请检查服务日志和 Hermes 的 Python 运行环境。',
|
||||
|
||||
title: '未检测到 hermes CLI',
|
||||
intro: 'hermes-ui 不再内置 hermes CLI,请先在运行本服务的机器上安装:',
|
||||
installCmd: 'pip install hermes-agent',
|
||||
|
||||
@@ -1,19 +1,24 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import { computed, onMounted, ref } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { checkHermesCliAvailable, resetHermesCliProbe } from "@/api/hermes/system";
|
||||
import { getHermesCliStatus, resetHermesCliProbe, type HermesCliStatus } from "@/api/hermes/system";
|
||||
|
||||
const { t } = useI18n();
|
||||
const router = useRouter();
|
||||
const rechecking = ref(false);
|
||||
const status = ref<HermesCliStatus | null>(null);
|
||||
const issue = computed(() => status.value?.reason === 'permission_denied' ? 'permission' : status.value?.reason === 'failed' ? 'failed' : 'missing');
|
||||
const title = computed(() => t(issue.value === 'missing' ? 'hermesMissing.title' : `hermesMissing.${issue.value}Title`));
|
||||
const intro = computed(() => t(issue.value === 'missing' ? 'hermesMissing.intro' : `hermesMissing.${issue.value}Intro`));
|
||||
onMounted(async () => { status.value = await getHermesCliStatus(); });
|
||||
|
||||
async function handleRetry() {
|
||||
rechecking.value = true;
|
||||
try {
|
||||
resetHermesCliProbe();
|
||||
const available = await checkHermesCliAvailable();
|
||||
if (available) {
|
||||
status.value = await getHermesCliStatus();
|
||||
if (status.value.available || status.value.reason === 'timeout') {
|
||||
router.replace("/hermes/chat");
|
||||
}
|
||||
} finally {
|
||||
@@ -27,17 +32,18 @@ async function handleRetry() {
|
||||
<div class="hermes-missing-card">
|
||||
<header class="hermes-missing-hd">
|
||||
<div class="hermes-missing-mark" aria-hidden="true">⚠</div>
|
||||
<h1 class="hermes-missing-title">{{ t("hermesMissing.title") }}</h1>
|
||||
<p class="hermes-missing-desc">{{ t("hermesMissing.intro") }}</p>
|
||||
<h1 class="hermes-missing-title">{{ title }}</h1>
|
||||
<p class="hermes-missing-desc">{{ intro }}</p>
|
||||
</header>
|
||||
|
||||
<div class="hermes-missing-bd">
|
||||
<pre class="hermes-missing-term"><code>{{ t("hermesMissing.installCmd") }}</code></pre>
|
||||
<pre v-if="issue === 'missing'" class="hermes-missing-term"><code>{{ t("hermesMissing.installCmd") }}</code></pre>
|
||||
<pre v-else-if="status?.bin" class="hermes-missing-term"><code>{{ status.bin }}</code></pre>
|
||||
|
||||
<p class="hermes-missing-desc">{{ t("hermesMissing.envIntro") }}</p>
|
||||
<pre class="hermes-missing-term"><code>{{ t("hermesMissing.envCmd") }}</code></pre>
|
||||
|
||||
<p class="hermes-missing-hint">{{ t("hermesMissing.restartHint") }}</p>
|
||||
<p class="hermes-missing-hint">{{ t(issue === 'missing' ? "hermesMissing.restartHint" : "hermesMissing.repairHint") }}</p>
|
||||
|
||||
<button class="hermes-missing-btn" :disabled="rechecking" @click="handleRetry">
|
||||
{{ rechecking ? "..." : t("hermesMissing.retry") }}
|
||||
|
||||
Reference in New Issue
Block a user