mirror of
https://github.com/R0m1k3/EveFlow.git
synced 2026-10-11 17:29:03 +02:00
fix: portail laissant passer /health mais bloquant /v1 traité comme liaison en échec (v2.4.0.3) (#18)
- 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 <noreply@anthropic.com>
This commit is contained in:
1 parent
df25dd591c
commit
d1b12c16c5
5 files changed
+26
-10
No files matched your search
@@ -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) |
|
||||
|
||||
+1
-1
@@ -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,
|
||||
|
||||
@@ -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}`;
|
||||
|
||||
@@ -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<boolean> {
|
||||
const client = new HermesClient({ ...config, url });
|
||||
for (const path of ['/v1/capabilities', '/v1/models', '/health']) {
|
||||
try {
|
||||
const payload = await client.request<unknown>(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<string | null> {
|
||||
for (const candidate of hermesUrlCandidates(config.url)) {
|
||||
onProgress?.(candidate);
|
||||
const client = new HermesClient({ ...config, url: candidate });
|
||||
try {
|
||||
await client.request<unknown>('/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;
|
||||
}
|
||||
|
||||
@@ -122,6 +122,8 @@ export const useHermes = create<HermesStore>((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);
|
||||
|
||||
Reference in new issue
Block a user