From d1b12c16c56096cd74dd20711b284d236cf6316f Mon Sep 17 00:00:00 2001 From: LogiFlow <101972532+R0m1k3@users.noreply.github.com> Date: Fri, 4 Sep 2026 08:22:19 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20portail=20laissant=20passer=20/health=20?= =?UTF-8?q?mais=20bloquant=20/v1=20trait=C3=A9=20comme=20liaison=20en=20?= =?UTF-8?q?=C3=A9chec=20(v2.4.0.3)=20(#18)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Une page web reçue sur /v1/capabilities fait échouer la connexion et le test de liaison même si /health répond, ce qui déclenche la recherche de l'API. - La découverte sonde /v1/capabilities puis /v1/models (JSON exigé, 401/403 accepté) avant /health, pour ne pas prendre un portail pour l'API. Claude-Session: https://claude.ai/code/session_017Wn5VX9HNbJ7N54hR24u9Y Co-authored-by: Claude --- docs/ROADMAP.md | 1 + package.json | 2 +- src/components/settings/SettingsDrawer.tsx | 1 + src/services/hermes/client.ts | 30 +++++++++++++++------- src/state/hermes.ts | 2 ++ 5 files changed, 26 insertions(+), 10 deletions(-) diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 36870de..e9c1978 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -77,6 +77,7 @@ Sources : [jarvis-desktop-ai](https://github.com/ccarloshenri/jarvis-desktop-ai) | Silero VAD sur phrase Kokoro de 2,3 s (2.3.0) | un seul segment, début et fin détectés, 6,7 s d'audio traités en 190 ms | | Capture d'écran → Hermes (2.3.0) | JPEG de 107 ko reçu côté Hermes (mock chat completions) | | Intention locale « coupe le son » (2.3.0) | traitée sans Hermes, résultat affiché dans le fil | +| Correctif 2.4.0.3 | un portail qui laisse passer /health mais renvoie une page web sur /v1 est traité comme une liaison en échec et déclenche la recherche de l'API (sondes /v1/capabilities, /v1/models) | | Correctif 2.4.0.2 | une page web (portail de connexion) reçue à la place de l'API Hermes est reconnue et expliquée ; l'API est recherchée automatiquement sur le même hôte (port 8642, /api, hermes.…) et l'URL corrigée | | Correctifs 2.4.0.1 | réponse vide en chat completions désormais expliquée (JSON non streamé ou erreur HTTP 200), la liaison ne passe plus en « dégradé » quand seule l'API des crons échoue, transcriptions parasites (« (cliquant) », « *Claire* ») ignorées, préférence de voix masculine/féminine | | Serveur MCP (2.4.0) | initialize, tools/list (14 outils), tools/call côté principal (presse-papiers, capture image) et côté renderer (état, message dans le fil) | diff --git a/package.json b/package.json index 10da26e..f12975e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "eveflow", "version": "2.4.0", - "releaseVersion": "2.4.0.2", + "releaseVersion": "2.4.0.3", "description": "JARVIS-style desktop HUD for Hermes Agent: voice, streaming runs, scheduled jobs, skills and telemetry", "main": "dist-electron/main.js", "private": true, diff --git a/src/components/settings/SettingsDrawer.tsx b/src/components/settings/SettingsDrawer.tsx index 5ccd804..de98da6 100644 --- a/src/components/settings/SettingsDrawer.tsx +++ b/src/components/settings/SettingsDrawer.tsx @@ -108,6 +108,7 @@ export function SettingsDrawer({ onClose }: Props) { caps = ` · modèle ${String(c.model ?? '?')} · ${Object.entries(c.features ?? {}).filter(([, v]) => v === true).length} fonctions`; } catch (err) { const message = (err as Error).message; + if (/page web/.test(message)) throw err; caps = /404/.test(message) ? ' · /v1/capabilities absent (Hermes ancien : transport chat completions)' : ` · capabilities : ${message}`; diff --git a/src/services/hermes/client.ts b/src/services/hermes/client.ts index 616aea1..6cb9f7a 100644 --- a/src/services/hermes/client.ts +++ b/src/services/hermes/client.ts @@ -72,18 +72,30 @@ export function hermesUrlCandidates(url: string): string[] { return [...out]; } -/** Probe candidate URLs until one answers Hermes JSON on /health or /v1/capabilities. */ +/** True when this base URL answers like the Hermes API (JSON on /v1/*, or 401/403 = key required). */ +export async function probeHermesUrl(config: HermesConfig, url: string): Promise { + const client = new HermesClient({ ...config, url }); + for (const path of ['/v1/capabilities', '/v1/models', '/health']) { + try { + const payload = await client.request(path, { timeoutMs: 4000 }); + if (payload && typeof payload === 'object') return true; + } catch (err) { + if (err instanceof HttpError) { + if (err.status === 401 || err.status === 403) return true; // the API is there, only the key is missing + if (/page web/.test(err.message)) return false; // a portal answers on this host: not the API + if (err.status === 404) continue; // older Hermes without this endpoint + } + return false; + } + } + return false; +} + +/** Probe candidate URLs until one answers like the Hermes API. */ export async function discoverHermesUrl(config: HermesConfig, onProgress?: (url: string) => void): Promise { for (const candidate of hermesUrlCandidates(config.url)) { onProgress?.(candidate); - const client = new HermesClient({ ...config, url: candidate }); - try { - await client.request('/health', { timeoutMs: 4000 }); - return candidate; - } catch (err) { - if (err instanceof HttpError && (err.status === 401 || err.status === 403)) return candidate; // API found, key missing - continue; - } + if (await probeHermesUrl(config, candidate)) return candidate; } return null; } diff --git a/src/state/hermes.ts b/src/state/hermes.ts index 5c788bc..c61db10 100644 --- a/src/state/hermes.ts +++ b/src/state/hermes.ts @@ -122,6 +122,8 @@ export const useHermes = create((set, get) => ({ try { capabilities = await client.capabilities(); } catch (err) { + // A web page on /v1/* means the whole API is behind a portal even if /health passed through. + if (/page web/.test((err as Error).message)) throw err; Log.warn('hermes', `capabilities unavailable: ${(err as Error).message}`); } const transport = resolveTransport(config, capabilities);