🐛 fix: the klavis in onboarding connect timeout fixed (#11918)

fix: the klavis in onboarding connect timeout fixed
This commit is contained in:
Shinji-Li
2026-01-28 03:23:41 +08:00
committed by GitHub
parent a074f486d7
commit bc165be510
6 changed files with 79 additions and 23 deletions

View File

@@ -104,7 +104,7 @@ const KlavisToolAuthItem = memo<KlavisToolAuthItemProps>(({ tool, onAuthComplete
try {
await refreshKlavisServerTools(identifier);
} catch (error) {
console.error('[Klavis] Failed to check auth status:', error);
console.debug('[Klavis] Polling check (expected during auth):', error);
}
}, POLL_INTERVAL_MS);
@@ -129,7 +129,8 @@ const KlavisToolAuthItem = memo<KlavisToolAuthItemProps>(({ tool, onAuthComplete
windowCheckIntervalRef.current = null;
}
oauthWindowRef.current = null;
refreshKlavisServerTools(identifier);
// Start polling after window closes
startFallbackPolling(identifier);
}
} catch {
if (windowCheckIntervalRef.current) {

View File

@@ -120,7 +120,7 @@ const KlavisSkillItem = memo<KlavisSkillItemProps>(({ serverType, server }) => {
try {
await refreshKlavisServerTools(serverName);
} catch (error) {
console.error('[Klavis] Failed to check auth status:', error);
console.debug('[Klavis] Polling check (expected during auth):', error);
}
}, POLL_INTERVAL_MS);
@@ -145,8 +145,8 @@ const KlavisSkillItem = memo<KlavisSkillItemProps>(({ serverType, server }) => {
windowCheckIntervalRef.current = null;
}
oauthWindowRef.current = null;
await refreshKlavisServerTools(serverName);
setIsWaitingAuth(false);
// Start polling after window closes
startFallbackPolling(serverName);
}
} catch {
console.log('[Klavis] COOP blocked window.closed access, falling back to polling');

View File

@@ -5,6 +5,7 @@ import { KlavisServerStatus } from '@/store/tool/slices/klavisStore';
const POLL_INTERVAL_MS = 1000;
const POLL_TIMEOUT_MS = 15_000;
const WINDOW_CLOSED_POLL_TIMEOUT_MS = 4000; // Shorter timeout when window is closed
interface UseKlavisOAuthProps {
serverStatus?: KlavisServerStatus;
@@ -50,14 +51,14 @@ export const useKlavisOAuth = ({ serverStatus }: UseKlavisOAuthProps) => {
}, [serverStatus, isWaitingAuth, cleanup]);
const startFallbackPolling = useCallback(
(serverName: string) => {
(serverName: string, timeoutMs: number = POLL_TIMEOUT_MS) => {
if (pollIntervalRef.current) return;
pollIntervalRef.current = setInterval(async () => {
try {
await refreshKlavisServerTools(serverName);
} catch (error) {
console.error('[Klavis] Failed to check auth status:', error);
console.debug('[Klavis] Polling check (expected during auth):', error);
}
}, POLL_INTERVAL_MS);
@@ -67,7 +68,7 @@ export const useKlavisOAuth = ({ serverStatus }: UseKlavisOAuthProps) => {
pollIntervalRef.current = null;
}
setIsWaitingAuth(false);
}, POLL_TIMEOUT_MS);
}, timeoutMs);
},
[refreshKlavisServerTools],
);
@@ -77,23 +78,29 @@ export const useKlavisOAuth = ({ serverStatus }: UseKlavisOAuthProps) => {
windowCheckIntervalRef.current = setInterval(() => {
try {
if (oauthWindow.closed) {
// Stop monitoring window
if (windowCheckIntervalRef.current) {
clearInterval(windowCheckIntervalRef.current);
windowCheckIntervalRef.current = null;
}
oauthWindowRef.current = null;
refreshKlavisServerTools(serverName);
// Start polling to check auth status after window closes
// Use shorter timeout since user has closed the window
// Keep loading state until we confirm success or timeout
startFallbackPolling(serverName, WINDOW_CLOSED_POLL_TIMEOUT_MS);
}
} catch {
if (windowCheckIntervalRef.current) {
clearInterval(windowCheckIntervalRef.current);
windowCheckIntervalRef.current = null;
}
// Use default timeout for fallback polling
startFallbackPolling(serverName);
}
}, 500);
},
[refreshKlavisServerTools, startFallbackPolling],
[startFallbackPolling],
);
const openOAuthWindow = useCallback(

View File

@@ -88,7 +88,7 @@ const KlavisServerItem = memo<KlavisServerItemProps>(
try {
await refreshKlavisServerTools(serverName);
} catch (error) {
console.error('[Klavis] Failed to check auth status:', error);
console.debug('[Klavis] Polling check (expected during auth):', error);
}
}, POLL_INTERVAL_MS);
@@ -121,8 +121,8 @@ const KlavisServerItem = memo<KlavisServerItemProps>(
}
oauthWindowRef.current = null;
// 窗口关闭后立即检查一次认证状态
refreshKlavisServerTools(serverName);
// 窗口关闭后开始轮询检查认证状态
startFallbackPolling(serverName);
}
} catch {
// COOP 阻止了访问,降级到轮询方案

View File

@@ -124,6 +124,7 @@ export const klavisRouter = router({
/**
* Get server instance status from Klavis API
* Returns error object instead of throwing on auth errors (useful for polling)
*/
getServerInstance: klavisProcedure
.input(
@@ -132,16 +133,43 @@ export const klavisRouter = router({
}),
)
.query(async ({ input, ctx }) => {
const response = await ctx.klavisClient.mcpServer.getServerInstance(input.instanceId);
return {
authNeeded: response.authNeeded,
externalUserId: response.externalUserId,
instanceId: response.instanceId,
isAuthenticated: response.isAuthenticated,
oauthUrl: response.oauthUrl,
platform: response.platform,
serverName: response.serverName,
};
try {
const response = await ctx.klavisClient.mcpServer.getServerInstance(input.instanceId);
return {
authNeeded: response.authNeeded,
error: undefined,
externalUserId: response.externalUserId,
instanceId: response.instanceId,
isAuthenticated: response.isAuthenticated,
oauthUrl: response.oauthUrl,
platform: response.platform,
serverName: response.serverName,
};
} catch (error) {
// Check if this is an authentication error
const errorMessage = error instanceof Error ? error.message : String(error);
const isAuthError =
errorMessage.includes('Invalid API key or instance ID') ||
errorMessage.includes('Status code: 401');
// For auth errors, return error object instead of throwing
// This prevents 500 errors in logs during polling
if (isAuthError) {
return {
authNeeded: true,
error: 'AUTH_ERROR',
externalUserId: undefined,
instanceId: input.instanceId,
isAuthenticated: false,
oauthUrl: undefined,
platform: undefined,
serverName: undefined,
};
}
// For other errors, still throw
throw error;
}
}),
getUserIntergrations: klavisProcedure

View File

@@ -210,10 +210,30 @@ export const createKlavisStoreSlice: StateCreator<
instanceId: server.instanceId,
});
// If server returned an auth error (during polling), silently return
// This happens when user is still in the process of authorizing
if (instanceStatus.error === 'AUTH_ERROR') {
set(
produce((draft: KlavisStoreState) => {
draft.loadingServerIds.delete(identifier);
}),
false,
n('refreshKlavisServerTools/pendingAuth'),
);
return;
}
// If authentication failed, remove server and reset status
if (!instanceStatus.isAuthenticated) {
if (!instanceStatus.authNeeded) {
// If no authentication needed, all is well
set(
produce((draft: KlavisStoreState) => {
draft.loadingServerIds.delete(identifier);
}),
false,
n('refreshKlavisServerTools/noAuthNeeded'),
);
return;
}