From 87eb5944d0090a6ee24570aaefbb4a9a01b51d17 Mon Sep 17 00:00:00 2001 From: lawrencecchen <54008264+lawrencecchen@users.noreply.github.com> Date: Mon, 2 Nov 2026 18:09:25 +0000 Subject: [PATCH 1/2] Cloud: the app server link's launcher and route live in CmuxNextCloudCredentials (fixes the tip arch red from ccb777fdde80) ccb777fdde80 (#32494) added +8 CmuxNextApp lines, which turned the tip arch-ratchet red ([app_lines] +8 over the last green bbb2b89006be). Fix forward, behavior identical: CloudService's choice of the app server link's launcher (none for the legacy link source, cmux link, or the Native-first launcher with cmux link as its fallback) and the `link_route` text each choice reports are CloudAppServerLink in CmuxNextCloudCredentials, next to CloudNativeFirstLauncher. The Native route holder is still made only for the Native choice of an app server link, and a Native fallback is still logged on app.cloud. CloudNativeFirstLauncherTests.theAppServerLinkReportsTheRouteItLaunched covers each branch: the launcher, the route text, and the route after a fallback. --- .../CmuxNextApp/Cloud/CloudService.swift | 26 ++++----------- .../CloudAppServerLink.swift | 32 +++++++++++++++++++ .../CloudNativeFirstLauncherTests.swift | 22 +++++++++++++ 3 files changed, 60 insertions(+), 20 deletions(-) create mode 100644 Packages/macOS/CmuxNext/Sources/CmuxNextCloudCredentials/CloudAppServerLink.swift diff --git a/Packages/macOS/CmuxNext/Sources/CmuxNextApp/Cloud/CloudService.swift b/Packages/macOS/CmuxNext/Sources/CmuxNextApp/Cloud/CloudService.swift index a622976d8924..47ef94aee114 100644 --- a/Packages/macOS/CmuxNext/Sources/CmuxNextApp/Cloud/CloudService.swift +++ b/Packages/macOS/CmuxNext/Sources/CmuxNextApp/Cloud/CloudService.swift @@ -239,27 +239,13 @@ final class CloudService { // The app server link: this Mac's `cmux link`, or with `CMUX_NEXT_CLOUD_NATIVE_LINK=1` the Native // device route (#28947) with `cmux link` as its fallback when Native does not start. let linkBuild = LaunchIdentity.current().build - let cmuxLink = CloudLinkLauncher.forApp(build: linkBuild, relay: relay) - let launcher: (any CloudLinkLaunching)? - if configuration.linkSource != .appServer { - launcher = nil - linkRoute = { "legacy" } - } else if CloudNativeFirstLauncher.choice() == .link { - launcher = cmuxLink - linkRoute = { "link" } - } else if let native = CloudNativeRouteHolder.forApp(identity: identity, auth: auth, paths: paths, - build: linkBuild, deviceName: Self.deviceName) { - let logger = Logger(subsystem: "com.cmuxterm.app.next", category: "app.cloud") - let first = CloudNativeFirstLauncher(native: native, link: cmuxLink) { error in - logger.error("cloud.link native route failed, using cmux link: \(String(describing: error), privacy: .public)") - } - launcher = first - linkRoute = { first.route } - } else { - launcher = cmuxLink - linkRoute = { "link (native asked; no Native route holder for this app)" } + let appServerLink = CloudAppServerLink(appServer: configuration.linkSource == .appServer, link: CloudLinkLauncher.forApp(build: linkBuild, relay: relay), + native: { CloudNativeRouteHolder.forApp(identity: identity, auth: auth, paths: paths, build: linkBuild, deviceName: Self.deviceName) }) { error in + Logger(subsystem: "com.cmuxterm.app.next", category: "app.cloud") + .error("cloud.link native route failed, using cmux link: \(String(describing: error), privacy: .public)") } - link = CloudLinkLifecycle(launcher: launcher, installID: { [identity] in await identity.installID() }, + linkRoute = appServerLink.route + link = CloudLinkLifecycle(launcher: appServerLink.launcher, installID: { [identity] in await identity.installID() }, connection: { [machines] in machines.local.connection }) binary = try? DaemonLauncher.resolveBinary(bundle: .main, environment: ProcessInfo.processInfo.environment) move.cli = binary diff --git a/Packages/macOS/CmuxNext/Sources/CmuxNextCloudCredentials/CloudAppServerLink.swift b/Packages/macOS/CmuxNext/Sources/CmuxNextCloudCredentials/CloudAppServerLink.swift new file mode 100644 index 000000000000..78a21913e8fb --- /dev/null +++ b/Packages/macOS/CmuxNext/Sources/CmuxNextCloudCredentials/CloudAppServerLink.swift @@ -0,0 +1,32 @@ +public import CmuxNextDaemon + +/// The app server link's launcher, and its route as launched for `cloud.machines`' `link_route`: `legacy`, +/// `link`, or the Native-first launcher's (``CloudNativeFirstLauncher/route``). +@MainActor +public struct CloudAppServerLink { + /// Nil for the legacy link (no app server link). + public let launcher: (any CloudLinkLaunching)? + public let route: @MainActor () -> String + + /// The launcher of an `appServer` link source: this Mac's `cmux link` (`link`), or with `choice` `.native` + /// (`CMUX_NEXT_CLOUD_NATIVE_LINK=1`, #28947) the Native device route `native()` makes, with `link` as its + /// fallback when Native does not start (`onFallback` says why). `native` runs only for that choice. + public init(appServer: Bool, link: (any CloudLinkLaunching)?, + choice: CloudNativeFirstLauncher.Choice = CloudNativeFirstLauncher.choice(), + native: () -> (any CloudLinkLaunching)?, onFallback: @escaping (any Error) -> Void) { + if !appServer { + launcher = nil + route = { "legacy" } + } else if choice == .link { + launcher = link + route = { "link" } + } else if let native = native() { + let first = CloudNativeFirstLauncher(native: native, link: link, onFallback: onFallback) + launcher = first + route = { first.route } + } else { + launcher = link + route = { "link (native asked; no Native route holder for this app)" } + } + } +} diff --git a/Packages/macOS/CmuxNext/Tests/CmuxNextAppTests/CloudNativeFirstLauncherTests.swift b/Packages/macOS/CmuxNext/Tests/CmuxNextAppTests/CloudNativeFirstLauncherTests.swift index fdf7479d8e8c..4f44eb1f3e0a 100644 --- a/Packages/macOS/CmuxNext/Tests/CmuxNextAppTests/CloudNativeFirstLauncherTests.swift +++ b/Packages/macOS/CmuxNext/Tests/CmuxNextAppTests/CloudNativeFirstLauncherTests.swift @@ -50,6 +50,28 @@ struct CloudNativeFirstLauncherTests { #expect(try await launcher.ensure(install: "i").status == "native") } + /// `cloud.machines`' `link_route` (#32494): the launcher each link source and choice gets, and its route. + @Test func theAppServerLinkReportsTheRouteItLaunched() async throws { + let link = FakeLauncher(status: "link") + var asked = 0 + let legacy = CloudAppServerLink(appServer: false, link: link, choice: .native, native: { asked += 1; return nil }) { _ in } + #expect(legacy.launcher == nil && legacy.route() == "legacy") + let cmuxLink = CloudAppServerLink(appServer: true, link: link, choice: .link, native: { asked += 1; return nil }) { _ in } + #expect(cmuxLink.launcher as? FakeLauncher === link && cmuxLink.route() == "link") + #expect(asked == 0, "the Native route holder was made for a choice that does not use it") + let noHolder = CloudAppServerLink(appServer: true, link: link, choice: .native, native: { asked += 1; return nil }) { _ in } + #expect(asked == 1 && noHolder.launcher as? FakeLauncher === link) + #expect(noHolder.route() == "link (native asked; no Native route holder for this app)") + + let native = FakeLauncher(status: "native", failure: NativeDeviceEnrollmentError.refused(400)) + var reasons = 0 + let first = CloudAppServerLink(appServer: true, link: link, choice: .native, native: { native }) { _ in reasons += 1 } + #expect(first.launcher is CloudNativeFirstLauncher && first.route() == "native") + #expect(try await first.launcher?.ensure(install: "i").status == "link") + #expect(reasons == 1) + #expect(first.route() == "link (native failed: \(NativeDeviceEnrollmentError.refused(400)))") + } + @Test func signedOutIsNotARouteFailure() async { let native = FakeLauncher(status: "native", failure: NativeDeviceEnrollmentError.signedOut) let link = FakeLauncher(status: "link") From 1db30cedad034319e55dd97098c0c6257dc268f4 Mon Sep 17 00:00:00 2001 From: lawrencecchen <54008264+lawrencecchen@users.noreply.github.com> Date: Mon, 2 Nov 2026 18:10:13 +0000 Subject: [PATCH 2/2] CloudAppServerLink: one assignment per branch --- .../CloudAppServerLink.swift | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/Packages/macOS/CmuxNext/Sources/CmuxNextCloudCredentials/CloudAppServerLink.swift b/Packages/macOS/CmuxNext/Sources/CmuxNextCloudCredentials/CloudAppServerLink.swift index 78a21913e8fb..a29561d85fd3 100644 --- a/Packages/macOS/CmuxNext/Sources/CmuxNextCloudCredentials/CloudAppServerLink.swift +++ b/Packages/macOS/CmuxNext/Sources/CmuxNextCloudCredentials/CloudAppServerLink.swift @@ -15,18 +15,14 @@ public struct CloudAppServerLink { choice: CloudNativeFirstLauncher.Choice = CloudNativeFirstLauncher.choice(), native: () -> (any CloudLinkLaunching)?, onFallback: @escaping (any Error) -> Void) { if !appServer { - launcher = nil - route = { "legacy" } + (launcher, route) = (nil, { "legacy" }) } else if choice == .link { - launcher = link - route = { "link" } + (launcher, route) = (link, { "link" }) } else if let native = native() { let first = CloudNativeFirstLauncher(native: native, link: link, onFallback: onFallback) - launcher = first - route = { first.route } + (launcher, route) = (first, { first.route }) } else { - launcher = link - route = { "link (native asked; no Native route holder for this app)" } + (launcher, route) = (link, { "link (native asked; no Native route holder for this app)" }) } } }