mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-17 23:27:41 +02:00
[eric] health: a mid-refresh 401 that outlives the token rotation window is a dead login; recheck at 240s and push the pill
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
5ac58919e5
commit
05e8310691
@@ -0,0 +1,29 @@
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import reducer, { healthReported, hideProviderHealthToast } from './subscriptionsSlice';
|
||||
|
||||
// A ChatGPT login whose token expired on 08-30 answered every probe with the router's "(reset after Ns)" 401 for six
|
||||
// days; the boot-time verdict excused it as mid-refresh and nothing ever looked again. The backend now re-probes after
|
||||
// the rotation window and pushes the verdict; the slice has to open the same pill the boot fetch would have.
|
||||
|
||||
test('a pushed verdict opens the reconnect pill', () => {
|
||||
const s = reducer(undefined, healthReported({ dead: [{ provider: 'codex', label: 'ChatGPT' }] }));
|
||||
assert.equal(s.healthToastOpen, true);
|
||||
assert.deepEqual(s.healthDead, [{ provider: 'codex', label: 'ChatGPT' }]);
|
||||
});
|
||||
|
||||
test('an empty verdict closes nothing the user already dismissed and opens nothing', () => {
|
||||
let s = reducer(undefined, healthReported({ dead: [{ provider: 'codex', label: 'ChatGPT' }] }));
|
||||
s = reducer(s, hideProviderHealthToast());
|
||||
s = reducer(s, healthReported({ dead: [] }));
|
||||
assert.equal(s.healthToastOpen, false);
|
||||
assert.deepEqual(s.healthDead, []);
|
||||
});
|
||||
|
||||
test('the socket routes subscriptions:health into the slice', () => {
|
||||
const src = fs.readFileSync(path.join(process.cwd(), 'src/shared/ws/WebSocketManager.ts'), 'utf8');
|
||||
assert.ok(src.includes("case 'subscriptions:health':"));
|
||||
assert.ok(src.includes('store.dispatch(healthReported({ dead: data.dead }))'));
|
||||
});
|
||||
@@ -72,6 +72,11 @@ const subscriptionsSlice = createSlice({
|
||||
state.status = action.payload;
|
||||
},
|
||||
// Optimistic: 9Router /providers lags /exchange, so refetching right after would clobber the just-connected state with stale data. The 30s poller reconciles.
|
||||
// The backend re-probes a "mid-refresh" 401 on its own after the rotation window and pushes the verdict here; the boot-time fetch is long gone by then.
|
||||
healthReported(state, action: PayloadAction<{ dead: DeadProvider[] }>) {
|
||||
state.healthDead = action.payload.dead ?? [];
|
||||
state.healthToastOpen = state.healthDead.length > 0 || state.healthCliMissing;
|
||||
},
|
||||
markSubscriptionConnected(state, action: PayloadAction<{ provider: string }>) {
|
||||
if (!state.status) return;
|
||||
const { provider } = action.payload;
|
||||
@@ -113,7 +118,7 @@ const subscriptionsSlice = createSlice({
|
||||
},
|
||||
});
|
||||
|
||||
export const { setSubscriptionStatus, markSubscriptionConnected, hideProviderHealthToast } = subscriptionsSlice.actions;
|
||||
export const { setSubscriptionStatus, markSubscriptionConnected, hideProviderHealthToast, healthReported } = subscriptionsSlice.actions;
|
||||
|
||||
// Stable empty ref so the selector doesn't hand back a fresh [] each call (forces needless rerenders).
|
||||
const EMPTY_CONNECTIONS: SubscriptionConnection[] = [];
|
||||
|
||||
@@ -34,6 +34,7 @@ import {
|
||||
} from '../state/agentsSlice';
|
||||
import { streamStart, streamSnapshot, streamDelta, streamEnd, clearStreamingForSession } from '../state/streamingSlice';
|
||||
import { fetchToolStatus } from '../state/toolsSlice';
|
||||
import { healthReported } from '../state/subscriptionsSlice';
|
||||
import { remountAppPreview } from '../state/outputsSlice';
|
||||
import { BackgroundDeltaBuffer } from './BackgroundDeltaBuffer';
|
||||
import { interactionActive, installInteractionListeners } from '../interactionPriority';
|
||||
@@ -657,6 +658,11 @@ class WebSocketManager {
|
||||
}
|
||||
break;
|
||||
|
||||
case 'subscriptions:health':
|
||||
if (Array.isArray(data.dead)) {
|
||||
store.dispatch(healthReported({ dead: data.dead }));
|
||||
}
|
||||
break;
|
||||
case 'tools:updated':
|
||||
// A connector's auth state changed on the backend (an OAuth claim landed, a disconnect); refetch it and tell the Tools page.
|
||||
if (data.tool_id) {
|
||||
|
||||
Reference in New Issue
Block a user