[eric] merge origin/eric/v2 — keep local ^0.85.0 bump of @softeria/ms-365-mcp-server over the incoming ^0.79.6 downgrade

This commit is contained in:
ciregenz
2026-04-21 22:17:08 -07:00
21 changed files with 1042 additions and 55 deletions
@@ -135,9 +135,33 @@ const OnboardingModal: React.FC = () => {
const [referralSourceOther, setReferralSourceOther] = useState<string>('');
const [connecting, setConnecting] = useState<string | null>(null);
const [nineRouterReady, setNineRouterReady] = useState<boolean | null>(null);
// Providers the backend says are already authenticated. Used to show a
// "Connected" state on rows where the user has already linked the account.
// Without this the row reverts to "Connect ->" after a 30s OAuth timeout
// even when the backend actually completed the link.
const [connectedProviders, setConnectedProviders] = useState<Set<string>>(new Set());
const pollTimerRef = useRef<any>(null);
const msgHandlerRef = useRef<any>(null);
// Poll subscription status while the connect step is showing so the row
// labels reflect any post-timeout backend success and any prior connections.
useEffect(() => {
if (step !== 'connect' || !open) return;
let cancelled = false;
const refresh = async () => {
try {
const r = await fetch(`${API_BASE}/agents/subscriptions/status`);
const d = await r.json();
if (cancelled) return;
const conns = d?.providers?.connections || [];
setConnectedProviders(new Set(conns.filter((p: any) => p.isActive).map((p: any) => p.provider)));
} catch {}
};
refresh();
const id = setInterval(refresh, 4000);
return () => { cancelled = true; clearInterval(id); };
}, [step, open]);
// Poll for 9Router readiness (it may still be starting when onboarding shows)
useEffect(() => {
let attempts = 0;
@@ -834,28 +858,31 @@ const OnboardingModal: React.FC = () => {
Use your existing subscription
</Typography>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 0.75, mb: 2.5 }}>
{SUBSCRIPTION_PROVIDERS.map((p) => (
{SUBSCRIPTION_PROVIDERS.map((p) => {
const isConnected = connectedProviders.has(p.id);
return (
<Box
key={p.id}
onClick={() => !p.preview && !connecting && nineRouterReady && handleConnect(p.id)}
onClick={() => !p.preview && !connecting && nineRouterReady && !isConnected && handleConnect(p.id)}
sx={{
display: 'flex', alignItems: 'center', justifyContent: 'space-between',
p: 1.5, borderRadius: `${c.radius.md}px`, border: `1px solid ${c.border.subtle}`,
cursor: p.preview || !nineRouterReady ? 'default' : connecting ? 'wait' : 'pointer',
p: 1.5, borderRadius: `${c.radius.md}px`, border: `1px solid ${isConnected ? c.accent.primary : c.border.subtle}`,
cursor: p.preview || !nineRouterReady || isConnected ? 'default' : connecting ? 'wait' : 'pointer',
opacity: p.preview ? 0.5 : !nineRouterReady ? 0.6 : 1,
transition: 'border-color 0.15s, background 0.15s',
...(!p.preview && nineRouterReady && { '&:hover': { borderColor: c.border.medium, bgcolor: `${c.accent.primary}05` } }),
...(!p.preview && nineRouterReady && !isConnected && { '&:hover': { borderColor: c.border.medium, bgcolor: `${c.accent.primary}05` } }),
}}
>
<Box>
<Typography sx={{ fontSize: '0.92rem', fontWeight: 600, color: c.text.primary }}>{p.name}</Typography>
<Typography sx={{ fontSize: '0.75rem', color: c.text.muted }}>{p.desc}</Typography>
</Box>
<Typography sx={{ fontSize: '0.78rem', color: p.preview ? c.text.ghost : connecting === p.id ? c.accent.primary : !nineRouterReady ? c.text.ghost : c.text.tertiary, fontStyle: p.preview ? 'italic' : 'normal' }}>
{p.preview ? 'Coming soon' : connecting === p.id ? 'Connecting...' : !nineRouterReady && nineRouterReady !== false ? 'Starting...' : 'Connect \u2192'}
<Typography sx={{ fontSize: '0.78rem', color: p.preview ? c.text.ghost : isConnected ? c.accent.primary : connecting === p.id ? c.accent.primary : !nineRouterReady ? c.text.ghost : c.text.tertiary, fontStyle: p.preview ? 'italic' : 'normal' }}>
{p.preview ? 'Coming soon' : isConnected ? 'Connected' : connecting === p.id ? 'Connecting...' : !nineRouterReady && nineRouterReady !== false ? 'Starting...' : 'Connect \u2192'}
</Typography>
</Box>
))}
);
})}
</Box>
{/* API key option */}