Skip to content

Commit 8d1b1c2

Browse files
author
kjgbot
committed
fix(auth): use production cloud and tolerate whoami latency
1 parent 89eec3a commit 8d1b1c2

2 files changed

Lines changed: 52 additions & 3 deletions

File tree

src/main/auth.test.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,11 @@ describe('getAccountWorkspaceId', () => {
127127
expect(mock.fetchMock).not.toHaveBeenCalled()
128128
})
129129

130+
it('uses the production cloud URL by default', async () => {
131+
const { getApiUrl } = await import('./auth')
132+
expect(getApiUrl()).toBe('https://agentrelay.com/cloud')
133+
})
134+
130135
it('returns the workspace id from currentWorkspace.id and persists the cache', async () => {
131136
writeAuthJson(userDataDir, {
132137
accessToken: 'cld_at_abc',
@@ -335,6 +340,47 @@ describe('getAccountWorkspaceId', () => {
335340
expect(mock.fetchMock).toHaveBeenCalledTimes(2)
336341
})
337342

343+
it('retries when whoami temporarily times out', async () => {
344+
writeAuthJson(userDataDir, {
345+
accessToken: 'cld_at_timeout_retry',
346+
refreshToken: 'cld_rt_timeout_retry',
347+
apiUrl: 'https://cloud.example'
348+
})
349+
mock.fetchMock
350+
.mockRejectedValueOnce(Object.assign(new Error('aborted'), { name: 'AbortError' }))
351+
.mockResolvedValueOnce({
352+
ok: true,
353+
status: 200,
354+
statusText: 'OK',
355+
json: async () => ({ currentWorkspace: { id: 'ws-after-timeout' } })
356+
})
357+
358+
const { getAccountWorkspaceId } = await import('./auth')
359+
await expect(getAccountWorkspaceId({ retryAttempts: 2, retryDelayMs: 0 })).resolves.toBe('ws-after-timeout')
360+
expect(mock.fetchMock).toHaveBeenCalledTimes(2)
361+
})
362+
363+
it('canonicalizes legacy agentrelay.dev auth records to production', async () => {
364+
writeAuthJson(userDataDir, {
365+
accessToken: 'cld_at_legacy_dev',
366+
refreshToken: 'cld_rt_legacy_dev',
367+
apiUrl: 'https://agentrelay.dev/cloud'
368+
})
369+
mock.fetchMock.mockResolvedValue({
370+
ok: true,
371+
status: 200,
372+
statusText: 'OK',
373+
json: async () => ({ currentWorkspace: { id: 'ws-prod' } })
374+
})
375+
376+
const { getAccountWorkspaceId } = await import('./auth')
377+
await expect(getAccountWorkspaceId()).resolves.toBe('ws-prod')
378+
expect(mock.fetchMock).toHaveBeenCalledWith(
379+
'https://agentrelay.com/cloud/api/v1/auth/whoami',
380+
expect.any(Object)
381+
)
382+
})
383+
338384
it('throws cloud-auth-required when whoami rejects the access token', async () => {
339385
writeAuthJson(userDataDir, {
340386
accessToken: 'cld_at_401',

src/main/auth.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ import {
1414
type UserInfo
1515
} from './schemas'
1616

17-
const CLOUD_API_URL = process.env.RELAY_CLOUD_URL || 'https://agentrelay.dev/cloud'
17+
const CLOUD_API_URL = process.env.RELAY_CLOUD_URL || 'https://agentrelay.com/cloud'
1818
const TOKEN_EXPIRY_BUFFER_MS = 60_000
19+
const WHOAMI_REQUEST_TIMEOUT_MS = 10_000
1920
const ACCOUNT_WORKSPACE_RETRY_ATTEMPTS = 8
2021
const ACCOUNT_WORKSPACE_RETRY_DELAY_MS = 500
2122
const warnedWhoamiWorkspaceFailures = new Set<string>()
@@ -277,7 +278,7 @@ function warnWhoamiWorkspaceFailure(failureClass: string): void {
277278

278279
async function fetchWhoamiPayload(apiUrl: string, accessToken: string): Promise<WhoamiPayloadResult> {
279280
const controller = new AbortController()
280-
const timeout = setTimeout(() => controller.abort(), 2500)
281+
const timeout = setTimeout(() => controller.abort(), WHOAMI_REQUEST_TIMEOUT_MS)
281282

282283
try {
283284
const res = await fetch(`${apiUrl}/api/v1/auth/whoami`, {
@@ -598,7 +599,9 @@ function cloudAuthFromStored(tokens: StoredTokens): CloudAuth {
598599
}
599600

600601
function normalizeCloudApiUrl(url: string | undefined): string {
601-
return (url || getApiUrl()).trim().replace(/\/+$/, '')
602+
const normalized = (url || getApiUrl()).trim().replace(/\/+$/, '')
603+
if (normalized === 'https://agentrelay.dev/cloud') return CLOUD_API_URL
604+
return normalized
602605
}
603606

604607
function readJwtPayload(token: string): Record<string, unknown> | null {

0 commit comments

Comments
 (0)