fix(ai): 网络失败时显示友好提示,不再直接展示 Failed to fetch

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
fucheng
2026-09-20 09:59:35 +08:00
parent b008307f82
commit 99052ada5f
11 changed files with 92 additions and 21 deletions
+15 -2
View File
@@ -11,6 +11,7 @@ import {
dbToVolume,
volumeToDb,
} from "./luxsinApi";
import { AI_NETWORK_ERROR_CODE, isLikelyNetworkFetchError } from "./networkError";
// Allow override via Vite env / localStorage; default to same origin in prod.
export function getAIBaseUrl(): string {
@@ -120,7 +121,13 @@ export interface QuestionPayload {
// REST helpers
// ============================================================
async function jsonFetch<T>(url: string, init?: RequestInit): Promise<T> {
const res = await fetch(url, init);
let res: Response;
try {
res = await fetch(url, init);
} catch (error) {
if (isLikelyNetworkFetchError(error)) throw new Error(AI_NETWORK_ERROR_CODE);
throw error;
}
if (!res.ok) throw new Error(`HTTP ${res.status}: ${await res.text()}`);
return res.json();
}
@@ -293,7 +300,7 @@ export function streamQuestion(payload: QuestionPayload, cb: StreamCallbacks): A
cb.onDone?.();
} catch (e: any) {
if (e?.name === "AbortError") return;
cb.onError?.(e?.message ?? String(e));
cb.onError?.(isLikelyNetworkFetchError(e) ? AI_NETWORK_ERROR_CODE : (e?.message ?? String(e)));
}
})();
@@ -472,6 +479,12 @@ export async function executeFrontendTool(
return { ok: false, message: `Unsupported tool: ${block.name}` };
}
} catch (e: any) {
if (isLikelyNetworkFetchError(e)) {
return {
ok: false,
message: "Device is not reachable. Ask the user to reconnect the device on the same network, then retry.",
};
}
return { ok: false, message: e?.message ?? String(e) };
}
}