()
| 53 | // ─── Health Polling ──────────────────────────────────────────── |
| 54 | |
| 55 | async function checkHealth() { |
| 56 | const base = getBaseUrl(); |
| 57 | if (!base) { |
| 58 | setDisconnected(); |
| 59 | return; |
| 60 | } |
| 61 | |
| 62 | // Retry loading auth token if we don't have one yet |
| 63 | if (!authToken) await loadAuthToken(); |
| 64 | |
| 65 | try { |
| 66 | const resp = await fetch(`${base}/health`, { signal: AbortSignal.timeout(3000) }); |
| 67 | if (!resp.ok) { setDisconnected(); return; } |
| 68 | const data = await resp.json(); |
| 69 | if (data.status === 'healthy') { |
| 70 | // Always refresh auth token from /health — the server generates a new |
| 71 | // token on each restart, so the old one becomes stale. |
| 72 | if (data.token) authToken = data.token; |
| 73 | // Forward chatEnabled so sidepanel can show/hide chat tab |
| 74 | setConnected({ ...data, chatEnabled: !!data.chatEnabled }); |
| 75 | } else { |
| 76 | setDisconnected(); |
| 77 | } |
| 78 | } catch (err) { |
| 79 | console.error('[gstack bg] Health check failed:', err.message); |
| 80 | setDisconnected(); |
| 81 | } |
| 82 | } |
| 83 | |
| 84 | function setConnected(healthData) { |
| 85 | const wasDisconnected = !isConnected; |
no test coverage detected