mirror of
https://github.com/stablyai/orca.git
synced 2026-11-02 10:08:13 +00:00
* fix(mobile): recover a relay that died during an app switch in one short probe, and retry an attach timeout through the director once Refs STA-11130. Co-Authored-By: Claude <noreply@anthropic.com> * fix(mobile): redial the same cell once on attach-timeout instead of re-resolving Aligns with the relay step 6 design: reason string attach-timeout, no director re-resolve (the director names the cell the host is seated on). Co-Authored-By: Claude <noreply@anthropic.com> * fix(mobile): probe decisively only on a real background return, 4s, and jitter the attach-timeout redial Co-Authored-By: Claude <noreply@anthropic.com> * test(mobile): replace type assertions with a typed fake in the relay probe tests Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
61 lines
2.5 KiB
TypeScript
61 lines
2.5 KiB
TypeScript
export const MOBILE_RELAY_CLOSE_CODE = {
|
|
BAD_OUTER_CREDENTIAL: 4401,
|
|
HOST_OFFLINE: 4404,
|
|
PEER_DROPPED: 4408,
|
|
WRONG_CELL: 4409,
|
|
LIMIT_EXCEEDED: 4429,
|
|
DRAINING: 4503
|
|
} as const
|
|
|
|
export type MobileRelayCloseCode =
|
|
(typeof MOBILE_RELAY_CLOSE_CODE)[keyof typeof MOBILE_RELAY_CLOSE_CODE]
|
|
|
|
// Mirror of RELAY_PHONE_CLOSE_REASON in cloud/packages/relay-contract. Close
|
|
// reasons on a phone socket that refine its close code; not RELAY_HOST_CLOSE_REASON,
|
|
// which names host control-socket closes. Peers that predate one never send it.
|
|
export const MOBILE_RELAY_PHONE_CLOSE_REASON = {
|
|
// A 4404 sent while the desktop's control looked alive but its data link
|
|
// missed the attach deadline; worth one quick redial of the same cell.
|
|
HOST_ATTACH_TIMEOUT: 'attach-timeout'
|
|
} as const
|
|
export type MobileRelayLeg = 'host-control' | 'host-data' | 'phone-invite' | 'phone-resume'
|
|
|
|
export type MobileRelayRecovery =
|
|
| { kind: 'disable-relay-credential'; directUnaffected: true }
|
|
| { kind: 'retry-after-host-offline'; fullJitter: true }
|
|
| { kind: 'reconnect-fresh-e2ee'; fullJitter: true }
|
|
| { kind: 'resolve-invite-through-director-ws'; requireStrictlyNewerEpoch: true }
|
|
| { kind: 'resolve-resume-through-director-post' }
|
|
| { kind: 'request-director-assignment' }
|
|
| { kind: 'backoff'; fullJitter: true }
|
|
| { kind: 'resolve-configured-director'; fullJitter: true }
|
|
|
|
export function mobileRelayRecoveryFor(
|
|
code: MobileRelayCloseCode,
|
|
leg: MobileRelayLeg
|
|
): MobileRelayRecovery {
|
|
switch (code) {
|
|
case MOBILE_RELAY_CLOSE_CODE.BAD_OUTER_CREDENTIAL:
|
|
return { kind: 'disable-relay-credential', directUnaffected: true }
|
|
case MOBILE_RELAY_CLOSE_CODE.HOST_OFFLINE:
|
|
return { kind: 'retry-after-host-offline', fullJitter: true }
|
|
case MOBILE_RELAY_CLOSE_CODE.PEER_DROPPED:
|
|
return { kind: 'reconnect-fresh-e2ee', fullJitter: true }
|
|
case MOBILE_RELAY_CLOSE_CODE.WRONG_CELL:
|
|
if (leg === 'phone-invite') {
|
|
return { kind: 'resolve-invite-through-director-ws', requireStrictlyNewerEpoch: true }
|
|
}
|
|
return leg === 'phone-resume'
|
|
? { kind: 'resolve-resume-through-director-post' }
|
|
: { kind: 'request-director-assignment' }
|
|
case MOBILE_RELAY_CLOSE_CODE.LIMIT_EXCEEDED:
|
|
return { kind: 'backoff', fullJitter: true }
|
|
case MOBILE_RELAY_CLOSE_CODE.DRAINING:
|
|
return { kind: 'resolve-configured-director', fullJitter: true }
|
|
}
|
|
}
|
|
|
|
export function isMobileRelayCloseCode(value: number): value is MobileRelayCloseCode {
|
|
return Object.values(MOBILE_RELAY_CLOSE_CODE).some((code) => code === value)
|
|
}
|