From 9d8ce97180ed58fb1fec554bd8bc7e20750cfed6 Mon Sep 17 00:00:00 2001 From: Lawrence Chen <54008264+lawrencecchen@users.noreply.github.com> Date: Tue, 18 Aug 2026 21:07:50 -0700 Subject: [PATCH 1/2] Merge MobileHostConnection initializers into one designated init The NWConnection initializer duplicated every stored-property assignment of the CmxByteTransport initializer. It is now a thin delegating initializer that wraps the accepted NWConnection in CmxNetworkByteTransport and forwards to the designated initializer, keeping only the parameters its callers (tests; production uses the transport initializer via acceptTransport) pass. --- Sources/Mobile/MobileHostService.swift | 33 ++++++++++---------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/Sources/Mobile/MobileHostService.swift b/Sources/Mobile/MobileHostService.swift index 90e376f9f794..8bdc0d15156e 100644 --- a/Sources/Mobile/MobileHostService.swift +++ b/Sources/Mobile/MobileHostService.swift @@ -2002,36 +2002,27 @@ actor MobileHostConnection { private var usableEventSubscription: UsableEventSubscription? private var didPublishUsableSession = false + /// Wraps an accepted `NWConnection` in the network byte transport and delegates. init( id: UUID, connection: NWConnection, - eventQueue: MobileHostConnectionEventQueue = MobileHostConnectionEventQueue(), firstFrameTimeoutNanoseconds: UInt64 = MobileHostConnection.defaultFirstFrameTimeoutNanoseconds, idleTimeoutNanoseconds: UInt64 = MobileHostConnection.defaultIdleTimeoutNanoseconds, - eventSendStallTimeoutNanoseconds: UInt64 = MobileHostConnection.defaultEventSendStallTimeoutNanoseconds, - independentEventWriter: (any MobileHostIndependentEventWriting)? = nil, authorizeRequest: @escaping @Sendable (MobileHostRPCRequest) async -> MobileHostRPCResult?, onAuthorizedRequest: @escaping @Sendable (MobileHostRPCRequest) async -> Void, - onUsableSession: @escaping @Sendable () async -> Bool = { true }, handleRequest: @escaping @Sendable (MobileHostRPCRequest) async -> MobileHostRPCResult, - onClose: @escaping @Sendable (UUID) async -> Void, - requestSimulatorFrameReplay: @escaping @Sendable (UUID, Set) async -> Void = { _, _ in } + onClose: @escaping @Sendable (UUID) async -> Void ) { - let transport = CmxNetworkByteTransport(acceptedConnection: connection) - self.id = id - self.transport = transport - self.writer = MobileHostSerializedTransportWriter(transport: transport) - self.independentEventWriter = independentEventWriter - self.firstFrameTimeoutNanoseconds = firstFrameTimeoutNanoseconds - self.idleTimeoutNanoseconds = idleTimeoutNanoseconds - self.eventSendStallTimeoutNanoseconds = eventSendStallTimeoutNanoseconds - self.authorizeRequest = authorizeRequest - self.onAuthorizedRequest = onAuthorizedRequest - self.onUsableSession = onUsableSession - self.handleRequest = handleRequest - self.onClose = onClose - self.requestSimulatorFrameReplay = requestSimulatorFrameReplay - self.eventQueue = eventQueue + self.init( + id: id, + transport: CmxNetworkByteTransport(acceptedConnection: connection), + firstFrameTimeoutNanoseconds: firstFrameTimeoutNanoseconds, + idleTimeoutNanoseconds: idleTimeoutNanoseconds, + authorizeRequest: authorizeRequest, + onAuthorizedRequest: onAuthorizedRequest, + handleRequest: handleRequest, + onClose: onClose + ) } init( From b0e810e0b2ed6f2cb35887688928f38dc592974b Mon Sep 17 00:00:00 2001 From: Lawrence Chen <54008264+lawrencecchen@users.noreply.github.com> Date: Tue, 18 Aug 2026 21:07:51 -0700 Subject: [PATCH 2/2] Normalize bare terminal.* RPC aliases before dispatch mobileHostHandleRPC listed each of the 8 terminal verbs twice per case (mobile.terminal. plus bare terminal.). A static alias set now maps exactly those 8 bare names to their canonical form before the switch, so each verb appears once. The accepted method-name set is unchanged; the advertised inventory in MobileHostService.irohReleaseGateRPCMethods is a hand-maintained list and is untouched. --- Sources/TerminalController.swift | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/Sources/TerminalController.swift b/Sources/TerminalController.swift index a2713339718f..789e4dcb0613 100644 --- a/Sources/TerminalController.swift +++ b/Sources/TerminalController.swift @@ -14398,6 +14398,11 @@ class TerminalController { // MARK: - Mobile Host V2 Methods + private static let bareMobileTerminalAliases: Set = [ + "terminal.create", "terminal.input", "terminal.paste", "terminal.paste_image", + "terminal.replay", "terminal.viewport", "terminal.scroll", "terminal.mouse", + ] + @MainActor func mobileHostHandleRPC( _ request: MobileHostRPCRequest, @@ -14413,7 +14418,10 @@ class TerminalController { // control socket shares the same bodies through `handleMobileHost`, so the // wire bytes stay identical across both entrypoints without a bridge here. let result: V2CallResult - switch request.method { + // Bare "terminal." aliases (still pinned in irohReleaseGateRPCMethods) normalize to "mobile.terminal.". + let method = Self.bareMobileTerminalAliases.contains(request.method) + ? "mobile." + request.method : request.method + switch method { case "mobile.host.status": result = v2MobileHostStatus(params: request.params, includePrivateMetadata: false) #if DEBUG @@ -14449,21 +14457,21 @@ class TerminalController { result = v2MobileTaskAttachmentUpload(params: request.params) case "mobile.task.models.list": result = await v2MobileTaskModelsList(params: request.params) - case "mobile.terminal.create", "terminal.create": + case "mobile.terminal.create": result = v2MobileTerminalCreate(params: request.params) - case "mobile.terminal.input", "terminal.input": + case "mobile.terminal.input": result = v2MobileTerminalInput(params: request.params) - case "mobile.terminal.paste", "terminal.paste": + case "mobile.terminal.paste": result = v2MobileTerminalPaste(params: request.params) - case "mobile.terminal.paste_image", "terminal.paste_image": + case "mobile.terminal.paste_image": result = v2MobileTerminalPasteImage(params: request.params) - case "mobile.terminal.replay", "terminal.replay": + case "mobile.terminal.replay": result = v2MobileTerminalReplay(params: request.params) - case "mobile.terminal.viewport", "terminal.viewport": + case "mobile.terminal.viewport": result = v2MobileTerminalViewport(params: request.params) - case "mobile.terminal.scroll", "terminal.scroll": + case "mobile.terminal.scroll": result = v2MobileTerminalScroll(params: request.params) - case "mobile.terminal.mouse", "terminal.mouse": + case "mobile.terminal.mouse": result = v2MobileTerminalMouse(params: request.params) case let method where method.hasPrefix("mobile.terminal.artifact."): result = await v2MobileTerminalArtifactDispatch(