Skip to content

Commit ed66ba5

Browse files
committed
fix antigravity refreshing
1 parent 0f2bd63 commit ed66ba5

File tree

1 file changed

+131
-48
lines changed

1 file changed

+131
-48
lines changed

src/auth/antigravity/fetch.ts

Lines changed: 131 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,11 @@ interface AttemptFetchOptions {
110110
thoughtSignature?: string
111111
}
112112

113+
type AttemptFetchResult = Response | null | "pass-through" | "needs-refresh"
114+
113115
async function attemptFetch(
114116
options: AttemptFetchOptions
115-
): Promise<Response | null | "pass-through"> {
117+
): Promise<AttemptFetchResult> {
116118
const { endpoint, url, init, accessToken, projectId, sessionId, modelName, thoughtSignature } =
117119
options
118120
debugLog(`Trying endpoint: ${endpoint}`)
@@ -183,6 +185,11 @@ async function attemptFetch(
183185
`[RESP] status=${response.status} content-type=${response.headers.get("content-type") ?? ""} url=${response.url}`
184186
)
185187

188+
if (response.status === 401) {
189+
debugLog(`[401] Unauthorized response detected, signaling token refresh needed`)
190+
return "needs-refresh"
191+
}
192+
186193
if (response.status === 403) {
187194
try {
188195
const text = await response.clone().text()
@@ -448,59 +455,135 @@ export function createAntigravityFetch(
448455
const thoughtSignature = getThoughtSignature(fetchInstanceId)
449456
debugLog(`[TSIG][GET] sessionId=${sessionId}, signature=${thoughtSignature ? thoughtSignature.substring(0, 20) + "..." : "none"}`)
450457

451-
for (let i = 0; i < maxEndpoints; i++) {
452-
const endpoint = ANTIGRAVITY_ENDPOINT_FALLBACKS[i]
453-
454-
const response = await attemptFetch({
455-
endpoint,
456-
url,
457-
init,
458-
accessToken: cachedTokens.access_token,
459-
projectId,
460-
sessionId,
461-
modelName,
462-
thoughtSignature,
463-
})
458+
let hasRefreshedFor401 = false
459+
460+
const executeWithEndpoints = async (): Promise<Response> => {
461+
for (let i = 0; i < maxEndpoints; i++) {
462+
const endpoint = ANTIGRAVITY_ENDPOINT_FALLBACKS[i]
463+
464+
const response = await attemptFetch({
465+
endpoint,
466+
url,
467+
init,
468+
accessToken: cachedTokens!.access_token,
469+
projectId,
470+
sessionId,
471+
modelName,
472+
thoughtSignature,
473+
})
464474

465-
if (response === "pass-through") {
466-
debugLog("Non-string body detected, passing through with auth headers")
467-
const headersWithAuth = {
468-
...init.headers,
469-
Authorization: `Bearer ${cachedTokens.access_token}`,
475+
if (response === "pass-through") {
476+
debugLog("Non-string body detected, passing through with auth headers")
477+
const headersWithAuth = {
478+
...init.headers,
479+
Authorization: `Bearer ${cachedTokens!.access_token}`,
480+
}
481+
return fetch(url, { ...init, headers: headersWithAuth })
470482
}
471-
return fetch(url, { ...init, headers: headersWithAuth })
472-
}
473483

474-
if (response) {
475-
debugLog(`Success with endpoint: ${endpoint}`)
476-
const transformedResponse = await transformResponseWithThinking(
477-
response,
478-
modelName || "",
479-
fetchInstanceId
480-
)
481-
return transformedResponse
484+
if (response === "needs-refresh") {
485+
if (hasRefreshedFor401) {
486+
debugLog("[401] Already refreshed once, returning unauthorized error")
487+
return new Response(
488+
JSON.stringify({
489+
error: {
490+
message: "Authentication failed after token refresh",
491+
type: "unauthorized",
492+
code: "token_refresh_failed",
493+
},
494+
}),
495+
{
496+
status: 401,
497+
statusText: "Unauthorized",
498+
headers: { "Content-Type": "application/json" },
499+
}
500+
)
501+
}
502+
503+
debugLog("[401] Refreshing token and retrying...")
504+
hasRefreshedFor401 = true
505+
506+
try {
507+
const newTokens = await refreshAccessToken(
508+
refreshParts.refreshToken,
509+
clientId,
510+
clientSecret
511+
)
512+
513+
cachedTokens = {
514+
type: "antigravity",
515+
access_token: newTokens.access_token,
516+
refresh_token: newTokens.refresh_token,
517+
expires_in: newTokens.expires_in,
518+
timestamp: Date.now(),
519+
}
520+
521+
clearProjectContextCache()
522+
523+
const formattedRefresh = formatTokenForStorage(
524+
newTokens.refresh_token,
525+
refreshParts.projectId || "",
526+
refreshParts.managedProjectId
527+
)
528+
529+
await client.set(providerId, {
530+
access: newTokens.access_token,
531+
refresh: formattedRefresh,
532+
expires: Date.now() + newTokens.expires_in * 1000,
533+
})
534+
535+
debugLog("[401] Token refreshed, retrying request...")
536+
return executeWithEndpoints()
537+
} catch (refreshError) {
538+
debugLog(`[401] Token refresh failed: ${refreshError instanceof Error ? refreshError.message : "Unknown error"}`)
539+
return new Response(
540+
JSON.stringify({
541+
error: {
542+
message: `Token refresh failed: ${refreshError instanceof Error ? refreshError.message : "Unknown error"}`,
543+
type: "unauthorized",
544+
code: "token_refresh_failed",
545+
},
546+
}),
547+
{
548+
status: 401,
549+
statusText: "Unauthorized",
550+
headers: { "Content-Type": "application/json" },
551+
}
552+
)
553+
}
554+
}
555+
556+
if (response) {
557+
debugLog(`Success with endpoint: ${endpoint}`)
558+
const transformedResponse = await transformResponseWithThinking(
559+
response,
560+
modelName || "",
561+
fetchInstanceId
562+
)
563+
return transformedResponse
564+
}
482565
}
566+
567+
const errorMessage = `All Antigravity endpoints failed after ${maxEndpoints} attempts`
568+
debugLog(errorMessage)
569+
570+
return new Response(
571+
JSON.stringify({
572+
error: {
573+
message: errorMessage,
574+
type: "endpoint_failure",
575+
code: "all_endpoints_failed",
576+
},
577+
}),
578+
{
579+
status: 503,
580+
statusText: "Service Unavailable",
581+
headers: { "Content-Type": "application/json" },
582+
}
583+
)
483584
}
484585

485-
// All endpoints failed
486-
const errorMessage = `All Antigravity endpoints failed after ${maxEndpoints} attempts`
487-
debugLog(errorMessage)
488-
489-
// Return error response
490-
return new Response(
491-
JSON.stringify({
492-
error: {
493-
message: errorMessage,
494-
type: "endpoint_failure",
495-
code: "all_endpoints_failed",
496-
},
497-
}),
498-
{
499-
status: 503,
500-
statusText: "Service Unavailable",
501-
headers: { "Content-Type": "application/json" },
502-
}
503-
)
586+
return executeWithEndpoints()
504587
}
505588
}
506589

0 commit comments

Comments
 (0)