AITURK IDE 1.0.0-beta.17: telefon kodu yayini yeniden denenir

Uzun sure acik kalan IDE'de "Telefon ekle" ilk istegi olu bir keep-alive
baglantisinda kaybedebiliyordu; bulusma kaydi yayinlanamayinca kod yolu bes
dakika boyunca "Kodla baglanma su an kullanilamiyor" kaliyordu (QR calisiyordu).

- publishPairOffer: en cok 4 deneme (0,5 / 1,5 / 3 sn); ag hatasi, 5xx ve 429
  yeniden denenir, 422 kalicidir; yaniti kaybolan denemeden sonraki 409 kaydin
  yerinde oldugu anlamina gelir (iptal jetonu yoktur, kayit kendiliginden biter).
- Pencerede "Yeniden dene": ayni kod ve QR ile kaydi yeniden yayinlar.
- Role oturumu kaydi ve "Giris yap" baslangici icin tek ek deneme (zaman asimi
  yeniden denenmez).
- Gunluge yalniz "pair offer published / failed after N tries" duser; kod,
  lookup ve jeton yazilmaz.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B8ppceaR5s6cvMnm7uGRnf
This commit is contained in:
2026-10-05 14:27:59 +03:00
co-authored by Claude Opus 5.5
parent 2ed9a6b2de
commit 58dbb4523b
13 changed files with 729 additions and 76 deletions
+62 -17
View File
@@ -92,24 +92,67 @@ export type RelayFailureCode =
| 'busy'
| 'timeout'
/** `POST /sessions`; a fixed failure code instead of any upstream text. */
export async function registerRelaySession(fetcher: RelayFetch): Promise<RelayCredentials | { error: RelayFailureCode }> {
try {
const response = await fetcher(`${MOBILE_RELAY_ORIGIN}${MOBILE_RELAY_PATH}/sessions`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
body: '{}',
redirect: 'error',
signal: AbortSignal.timeout(MOBILE_RELAY_FETCH_TIMEOUT_MS)
})
/** Wait before the single second try of a request whose first try never got an answer. */
export const MOBILE_NETWORK_RETRY_DELAY_MS = 500
if (response.status !== 201) {
return { error: response.status === 429 ? 'rate_limited' : 'register_failed' }
/** An aborted / timed-out request already used its whole budget: it is not tried again. */
export function isTimeoutError(error: unknown): boolean {
const name = (error as null | { name?: unknown })?.name
return name === 'TimeoutError' || name === 'AbortError'
}
export interface NetworkRetryOptions {
/** Injected wait (tests: fake clock). Default: a real timer. */
sleep?: (ms: number) => Promise<void>
retryDelayMs?: number
}
const realSleep = (ms: number) => new Promise<void>(resolve => setTimeout(resolve, ms))
/**
* `POST /sessions`; a fixed failure code instead of any upstream text.
*
* A process that sat idle for a long time can fail its first request on a dead
* pooled keep-alive connection. So a THROWN network error (no HTTP answer at
* all) is tried once more after ~0.5 s. Any HTTP answer is final: no retry.
*/
export async function registerRelaySession(
fetcher: RelayFetch,
options: NetworkRetryOptions = {}
): Promise<RelayCredentials | { error: RelayFailureCode }> {
const sleep = options.sleep ?? realSleep
for (let attempt = 1; ; attempt++) {
let response: RelayFetchResponse
try {
response = await fetcher(`${MOBILE_RELAY_ORIGIN}${MOBILE_RELAY_PATH}/sessions`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
body: '{}',
redirect: 'error',
signal: AbortSignal.timeout(MOBILE_RELAY_FETCH_TIMEOUT_MS)
})
} catch (error) {
if (attempt >= 2 || isTimeoutError(error)) {
return { error: 'register_failed' }
}
await sleep(options.retryDelayMs ?? MOBILE_NETWORK_RETRY_DELAY_MS)
continue
}
return parseRelaySession(await response.json()) ?? { error: 'register_failed' }
} catch {
return { error: 'register_failed' }
try {
if (response.status !== 201) {
return { error: response.status === 429 ? 'rate_limited' : 'register_failed' }
}
return parseRelaySession(await response.json()) ?? { error: 'register_failed' }
} catch {
return { error: 'register_failed' }
}
}
}
@@ -221,6 +264,8 @@ export function createRelayClient(deps: RelayClientDeps) {
let retryTimer: unknown = null
let silenceTimer: unknown = null
const streams = new Map<number, Stream>()
/** Same (injectable) clock as every other wait of this client. */
const sleep = (ms: number) => new Promise<void>(resolve => void setTimer(resolve, ms))
function setState(status: RelayStatus, code: null | RelayFailureCode = null) {
if (state.status === status && state.code === code) {
@@ -270,7 +315,7 @@ export function createRelayClient(deps: RelayClientDeps) {
return stored
}
const fresh = await registerRelaySession(deps.fetch)
const fresh = await registerRelaySession(deps.fetch, { sleep })
if ('error' in fresh) {
return fresh
@@ -626,7 +671,7 @@ export function createRelayClient(deps: RelayClientDeps) {
* only once the new one is stored; false = nothing changed.
*/
async renew(): Promise<boolean> {
const fresh = await registerRelaySession(deps.fetch)
const fresh = await registerRelaySession(deps.fetch, { sleep })
if ('error' in fresh) {
return false