(function initializeCognitiveAgentNetwork() { const nativeFetch = window.fetch.bind(window); const listeners = new Set(); const state = { status: navigator.onLine === false ? "offline" : "connecting", lastSuccessAt: null, lastError: null, }; let probeTimer = null; function snapshot() { return { ...state }; } function renderBadge() { const badge = document.querySelector("#connectionBadge"); if (!badge) return; const presentation = { connecting: ["neutral", "loader-circle", "连接中"], online: ["", "wifi", "服务已连接"], degraded: ["warning", "wifi-low", "连接不稳定"], offline: ["danger", "wifi-off", "服务已断开"], }[state.status]; badge.className = `status-badge connection-badge ${presentation[0]}`.trim(); badge.innerHTML = `${presentation[2]}`; badge.title = state.lastError || presentation[2]; window.lucide?.createIcons?.({ attrs: { "aria-hidden": "true" }, nodes: [badge], }); } function publish(status, error = null) { const changed = state.status !== status || state.lastError !== error; state.status = status; state.lastError = error; if (status === "online") state.lastSuccessAt = new Date().toISOString(); renderBadge(); if (!changed) return; const detail = snapshot(); listeners.forEach((listener) => listener(detail)); window.dispatchEvent(new CustomEvent("cognitive-agent:connection", { detail })); } function sleep(milliseconds) { return new Promise((resolve) => window.setTimeout(resolve, milliseconds)); } async function request(input, options = {}) { const { timeoutMs = 10000, retries: configuredRetries, signal: callerSignal, ...fetchOptions } = options; const method = String(fetchOptions.method || "GET").toUpperCase(); const retries = configuredRetries ?? (method === "GET" ? 1 : 0); let lastError; for (let attempt = 0; attempt <= retries; attempt += 1) { const controller = new AbortController(); const abortFromCaller = () => controller.abort(callerSignal?.reason); callerSignal?.addEventListener("abort", abortFromCaller, { once: true }); const timeout = window.setTimeout(() => controller.abort("timeout"), timeoutMs); try { const response = await nativeFetch(input, { ...fetchOptions, signal: controller.signal, }); publish(response.status >= 500 ? "degraded" : "online", response.status >= 500 ? `服务返回 HTTP ${response.status}` : null); return response; } catch (error) { lastError = error; const message = controller.signal.aborted && !callerSignal?.aborted ? `请求超时(${Math.round(timeoutMs / 1000)} 秒)` : (error?.message || "网络请求失败"); publish(attempt < retries ? "degraded" : "offline", message); if (callerSignal?.aborted || attempt >= retries || method !== "GET") { throw new Error(message, { cause: error }); } await sleep(300 * (attempt + 1)); } finally { window.clearTimeout(timeout); callerSignal?.removeEventListener("abort", abortFromCaller); } } throw lastError; } async function probe() { if (document.hidden) return; if (navigator.onLine === false) { publish("offline", "设备当前处于离线状态"); return; } try { const response = await request("/api/health", { cache: "no-store", timeoutMs: 4000, retries: 0, }); if (!response.ok && response.status !== 401) { publish("degraded", `健康检查返回 HTTP ${response.status}`); } } catch (_error) { // request() already published the detailed state. } } function startMonitoring() { if (probeTimer !== null) return; renderBadge(); probe(); probeTimer = window.setInterval(probe, 10000); window.addEventListener("online", () => { publish("connecting"); probe(); }); window.addEventListener("offline", () => publish("offline", "设备当前处于离线状态")); document.addEventListener("visibilitychange", () => { if (!document.hidden) probe(); }); } window.CognitiveAgentNetwork = { request, probe, subscribe(listener) { listeners.add(listener); listener(snapshot()); return () => listeners.delete(listener); }, getState: snapshot, nativeFetch, }; document.addEventListener("DOMContentLoaded", startMonitoring, { once: true }); })();