mirror of
https://github.com/stablyai/orca.git
synced 2026-10-19 08:03:13 +00:00
* feat(mobile): define relay protocol groundwork Co-authored-by: Orca <help@stably.ai> * feat(mobile): implement replay-safe E2EE v2 sessions Co-authored-by: Orca <help@stably.ai> * test(auth): lock cloud refresh single-flight Co-authored-by: Orca <help@stably.ai> * test(mobile): complete E2EE v2 adversarial coverage Co-authored-by: Orca <help@stably.ai> * refactor(runtime): unify mobile socket wiring Co-authored-by: Orca <help@stably.ai> * feat(runtime): add relay control and data clients Co-authored-by: Orca <help@stably.ai> * feat(runtime): coordinate desktop relay sessions Co-authored-by: Orca <help@stably.ai> * fix(auth): fence stale cloud session mutations Co-authored-by: Orca <help@stably.ai> * feat(runtime): add relay pairing and durable revoke Co-authored-by: Orca <help@stably.ai> * feat(runtime): add relay credential pairing RPCs Co-authored-by: Orca <help@stably.ai> * feat(settings): show Orca Relay sign-in status Co-authored-by: Orca <help@stably.ai> * test(relay): prove desktop lifecycle and E2EE splice Co-authored-by: Orca <help@stably.ai> * feat(mobile): persist relay pairing state Co-authored-by: Orca <help@stably.ai> * feat(mobile): race direct and relay pairing Co-authored-by: Orca <help@stably.ai> * feat(mobile): recover pairing through relay director Co-authored-by: Orca <help@stably.ai> * fix(relay): preserve origin controls during drain Co-authored-by: Orca <help@stably.ai> * feat(mobile): recover interrupted relay pairing Co-authored-by: Orca <help@stably.ai> * feat(mobile): add stable relay RPC sessions Co-authored-by: Orca <help@stably.ai> * feat(mobile): supervise direct and relay endpoints Co-authored-by: Orca <help@stably.ai> * Cover mobile relay director fallback matrix Co-authored-by: Orca <help@stably.ai> * Fix relay settings component test isolation Co-authored-by: Orca <help@stably.ai> * Remove unrelated merge formatting drift Co-authored-by: Orca <help@stably.ai> * Update runtime connection count integration assertion Co-authored-by: Orca <help@stably.ai> * Run mobile typecheck through pnpm Co-authored-by: Orca <help@stably.ai> * feat(relay): gate desktop controls on mobile demand Co-authored-by: Orca <help@stably.ai> * test(mobile): cover served relay recovery Co-authored-by: Orca <help@stably.ai> * feat(mobile): upgrade direct pairings to relay Co-authored-by: Orca <help@stably.ai> * fix(relay): harden mobile reconnect and teardown Co-authored-by: Orca <help@stably.ai> * fix(auth): clarify account sign-in state Co-authored-by: Orca <help@stably.ai> * fix(auth): polish sign-in completion flow Co-authored-by: Orca <help@stably.ai> * fix(auth): clarify sign-out confirmation Co-authored-by: Orca <help@stably.ai> * fix(auth): simplify sign-in completion page Co-authored-by: Orca <help@stably.ai> * feat(mobile): add per-device pairing connection mode Co-authored-by: Orca <help@stably.ai> * fix(mobile): stabilize pairing option layout Co-authored-by: Orca <help@stably.ai> * fix(mobile): give pairing choices stable space Co-authored-by: Orca <help@stably.ai> * fix(mobile): stabilize pairing QR regeneration Co-authored-by: Orca <help@stably.ai> * Animate mobile pairing flow height Co-authored-by: Orca <help@stably.ai> * Configure auth in packaged builds Co-authored-by: Orca <help@stably.ai> * Make Orca Relay pairing an opt-in beta Co-authored-by: Orca <help@stably.ai> * Show Relay beta details on hover Co-authored-by: Orca <help@stably.ai> * Refine mobile relay pairing choice Co-authored-by: Orca <help@stably.ai> * Polish Orca Relay pairing controls Co-authored-by: Orca <help@stably.ai> * Keep mobile contract fallback test additive Co-authored-by: Orca <help@stably.ai> --------- Co-authored-by: Orca <help@stably.ai>
75 lines
2.2 KiB
TypeScript
75 lines
2.2 KiB
TypeScript
import {
|
|
PAIRING_OFFER_VERSION,
|
|
PairingOfferSchema,
|
|
type PairingOffer
|
|
} from './mobile-relay-pairing-offer'
|
|
|
|
export { PAIRING_OFFER_VERSION, PairingOfferSchema }
|
|
export type { PairingOffer }
|
|
|
|
export function encodePairingOffer(offer: PairingOffer): string {
|
|
const json = JSON.stringify(offer)
|
|
const base64url = Buffer.from(json, 'utf-8')
|
|
.toString('base64')
|
|
.replace(/\+/g, '-')
|
|
.replace(/\//g, '_')
|
|
.replace(/=+$/, '')
|
|
// Why: Android camera intents and Expo Router preserve query params more
|
|
// reliably than URL fragments when launching a custom-scheme app.
|
|
return `orca://pair?code=${base64url}`
|
|
}
|
|
|
|
export function decodePairingOffer(url: string): PairingOffer {
|
|
const code = extractPairingCodeFromUrl(url)
|
|
if (!code) {
|
|
throw new Error('Invalid pairing URL: must start with orca://pair and include a pairing code')
|
|
}
|
|
return decodePairingBase64(code)
|
|
}
|
|
|
|
function extractPairingCodeFromUrl(url: string): string | null {
|
|
let parsed: URL
|
|
try {
|
|
parsed = new URL(url)
|
|
} catch {
|
|
return null
|
|
}
|
|
// Why: prefix checks accepted routes like `orca://pairing?...`; only the
|
|
// pairing deep-link host may carry runtime auth material.
|
|
if (parsed.protocol !== 'orca:' || parsed.hostname !== 'pair') {
|
|
return null
|
|
}
|
|
if (parsed.pathname !== '' && parsed.pathname !== '/') {
|
|
return null
|
|
}
|
|
const code = parsed.searchParams.get('code')
|
|
if (code) {
|
|
return code
|
|
}
|
|
return parsed.hash ? parsed.hash.slice(1) || null : null
|
|
}
|
|
|
|
// Why: accept either an `orca://pair?...` URL or the bare base64
|
|
// string so the mobile paste-pair flow can take whichever the user
|
|
// actually copied from desktop.
|
|
export function parsePairingCode(input: string): PairingOffer | null {
|
|
const trimmed = input.trim()
|
|
if (!trimmed) {
|
|
return null
|
|
}
|
|
try {
|
|
if (trimmed.toLowerCase().startsWith('orca://')) {
|
|
return decodePairingOffer(trimmed)
|
|
}
|
|
return decodePairingBase64(trimmed)
|
|
} catch {
|
|
return null
|
|
}
|
|
}
|
|
|
|
function decodePairingBase64(base64url: string): PairingOffer {
|
|
const base64 = base64url.replace(/-/g, '+').replace(/_/g, '/')
|
|
const json = Buffer.from(base64, 'base64').toString('utf-8')
|
|
return PairingOfferSchema.parse(JSON.parse(json))
|
|
}
|