Files
orca/src/cli/runtime/remote-runtime-compat-gate.ts
T
OrcaWinandm4air 34e2e5f469 refactor(remote): clients connect through a paired-connection handle, not a pairing offer (#27533)
Desktop, CLI, web and runtime transports now take a PairedRuntimeConnection
(endpoint, pinned host key, credential) instead of a PairingOffer. Offers stay on
the pairing input path (codes, QR, links, environment add) and are converted with
pairedRuntimeConnectionFromOffer. The credential is token-only today; request
envelopes still carry the same deviceToken, so the wire is unchanged.

This lets device-key and ticket credentials replace the token later without
touching every connect call site again.

Co-authored-by: m4air <m4air@Mac.localdomain>
2026-10-11 14:02:03 -07:00

80 lines
2.7 KiB
TypeScript

import type { PairedRuntimeConnection } from '../../shared/paired-runtime-connection'
import { describeRuntimeCompatBlock, evaluateRuntimeCompat } from '../../shared/protocol-compat'
import {
MIN_COMPATIBLE_RUNTIME_SERVER_VERSION,
RUNTIME_PROTOCOL_VERSION
} from '../../shared/protocol-version'
import type { RuntimeOrchestrationEnvelope } from '../../shared/runtime-rpc-envelope'
import type { RuntimeStatus } from '../../shared/runtime-types'
import { markEnvironmentUsed } from './environments'
import { RuntimeClientError, RuntimeRpcFailureError, type RuntimeRpcResponse } from './types'
import type {
sendWebSocketRequest,
sendWebSocketRequestWithStatusPreflight
} from './websocket-transport'
type WebSocketTransport = {
sendWebSocketRequest: typeof sendWebSocketRequest
sendWebSocketRequestWithStatusPreflight: typeof sendWebSocketRequestWithStatusPreflight
}
export class RemoteRuntimeCompatGate {
private checked = false
constructor(
private readonly userDataPath: string,
private readonly environmentSelector: string | null
) {}
send<TResult>(args: {
transport: WebSocketTransport
connection: PairedRuntimeConnection
method: string
params: unknown
timeoutMs: number
envelope?: RuntimeOrchestrationEnvelope
}): Promise<RuntimeRpcResponse<TResult>> {
if (this.checked || args.method === 'status.get') {
return args.transport.sendWebSocketRequest<TResult>(
args.connection,
args.method,
args.params,
args.timeoutMs,
args.envelope
)
}
return args.transport.sendWebSocketRequestWithStatusPreflight<TResult>(
args.connection,
args.method,
args.params,
args.timeoutMs,
(response) => {
if (response.ok === false) {
throw new RuntimeRpcFailureError(response)
}
this.noteVerifiedStatus(response.result)
if (this.environmentSelector) {
markEnvironmentUsed(this.userDataPath, this.environmentSelector, {
runtimeId: response._meta.runtimeId
})
}
},
args.envelope
)
}
noteVerifiedStatus(status: RuntimeStatus): void {
const verdict = evaluateRuntimeCompat({
clientProtocolVersion: RUNTIME_PROTOCOL_VERSION,
minCompatibleServerProtocolVersion: MIN_COMPATIBLE_RUNTIME_SERVER_VERSION,
serverProtocolVersion: status.runtimeProtocolVersion ?? status.protocolVersion,
serverMinCompatibleClientProtocolVersion:
status.minCompatibleRuntimeClientVersion ?? status.minCompatibleMobileVersion
})
if (verdict.kind === 'blocked') {
throw new RuntimeClientError('incompatible_runtime', describeRuntimeCompatBlock(verdict))
}
this.checked = true
}
}