Merge pull request #10101 from manaflow-ai/issue-10060-mosh-bootstrap-misreport

Fix mosh bootstrap staging: real argv for mosh-server, stage-specific errors, proxy address fallback
This commit is contained in:
Austin Wang
2026-08-13 14:42:51 -07:00
committed by GitHub
12 changed files with 875 additions and 71 deletions
+8
View File
@@ -78,6 +78,14 @@ extension CMUXCLI {
remoteMoshProbeFailedMessage: String(
localized: "cli.ssh.mosh.probeFailed",
defaultValue: "[cmux] Could not verify remote Mosh support; continuing over SSH."
),
remoteBootstrapInstallFailedMessage: String(
localized: "cli.ssh.mosh.bootstrapInstallFailed",
defaultValue: "[cmux] Remote bootstrap install failed; continuing over SSH."
),
remoteMoshAddressFallbackMessage: String(
localized: "cli.ssh.mosh.addressFallback",
defaultValue: "[cmux] Remote SSH advertised an unusable address; resolving the Mosh address through the SSH connection."
)
).command()
}
+4 -1
View File
@@ -16,6 +16,7 @@ so call sites read naturally (`value.javaScriptStringLiteral`, not `f(value)`).
- `String.javaScriptStringLiteral` — the string encoded as a quoted JavaScript string literal.
- `SSHAgentSocketResolver` — OpenSSH option parsing and SSH agent socket path normalization.
- `MoshTerminalCommandBuilder` — a pure Mosh startup-command builder with explicit SSH fallback.
- `MoshRemoteIPMode` — the address-discovery mode selected for a Mosh connection.
- `RemoteTmuxCommandBuilder` — shared remote `tmux` resolution and argv preservation.
- `WorkspaceRemoteTerminalProfile` — durable shell-or-named-tmux terminal intent.
- `WorkspaceRemoteTerminalTransport` — the persisted SSH-or-Mosh interactive terminal preference.
@@ -51,7 +52,9 @@ let command = MoshTerminalCommandBuilder(
localMoshMissingMessage: "Mosh is unavailable locally; using SSH.",
localMoshUnsupportedMessage: "Mosh is too old for shared SSH setup; using SSH.",
remoteMoshMissingMessage: "mosh-server is unavailable remotely; using SSH.",
remoteMoshProbeFailedMessage: "Mosh capability check failed; using SSH."
remoteMoshProbeFailedMessage: "Mosh capability check failed; using SSH.",
remoteBootstrapInstallFailedMessage: "Remote bootstrap install failed; using SSH.",
remoteMoshAddressFallbackMessage: "Remote SSH address is unusable; using local Mosh resolution."
).command()
```
@@ -0,0 +1,15 @@
/// Selects how Mosh discovers the address used for its UDP session.
///
/// Production callers always start from ``remote``; the launcher downgrades
/// to ``proxy`` automatically when SSH advertises an unusable address. The
/// other cases exist so tests can pin each generated mode explicitly.
public enum MoshRemoteIPMode: String, Equatable, Sendable {
/// Derive the address from the remote SSH connection when possible.
case remote
/// Resolve the destination locally before starting the Mosh server.
case local
/// Resolve the address through Mosh's local proxy path.
case proxy
}
@@ -12,6 +12,7 @@ public struct MoshTerminalCommandBuilder: Sendable {
private let destination: String
private let remoteCommandArguments: [String]
private let remoteRelayPort: Int?
private let remoteIPMode: MoshRemoteIPMode
private let preparationShellScript: String?
private let managementReadyShellScript: String?
private let sshFallbackCommand: String
@@ -19,6 +20,8 @@ public struct MoshTerminalCommandBuilder: Sendable {
private let localMoshUnsupportedMessage: String
private let remoteMoshMissingMessage: String
private let remoteMoshProbeFailedMessage: String
private let remoteBootstrapInstallFailedMessage: String
private let remoteMoshAddressFallbackMessage: String
/// Creates a Mosh terminal command builder.
///
@@ -29,13 +32,16 @@ public struct MoshTerminalCommandBuilder: Sendable {
/// - destination: SSH destination or host alias.
/// - remoteCommandArguments: Optional command argv launched by `mosh-server`.
/// - remoteRelayPort: Optional remote relay whose presence enables authoritative lifecycle attempt registration.
/// - preparationShellScript: Optional local preparation run after capability checks.
/// - remoteIPMode: Address-discovery mode passed to Mosh; remote mode falls back to SSH proxy resolution when SSH advertises an unusable address.
/// - preparationShellScript: Optional local preparation run before capability checks.
/// - managementReadyShellScript: Optional local callback run after SSH preparation succeeds and before Mosh starts.
/// - sshFallbackCommand: Complete local SSH terminal command used when Mosh is unavailable.
/// - localMoshMissingMessage: User-facing message printed when no local `mosh` executable exists.
/// - localMoshUnsupportedMessage: User-facing message printed when local Mosh lacks the required remote-IP mode.
/// - remoteMoshMissingMessage: User-facing message printed when `mosh-server` is absent remotely.
/// - remoteMoshProbeFailedMessage: User-facing message printed when the remote capability probe fails.
/// - remoteBootstrapInstallFailedMessage: User-facing message printed when bootstrap staging fails.
/// - remoteMoshAddressFallbackMessage: User-facing message printed when SSH proxy address resolution is selected automatically.
public init(
capabilityProbeSSHArguments: [String],
sessionSSHArguments: [String],
@@ -43,13 +49,16 @@ public struct MoshTerminalCommandBuilder: Sendable {
destination: String,
remoteCommandArguments: [String],
remoteRelayPort: Int? = nil,
remoteIPMode: MoshRemoteIPMode = .remote,
preparationShellScript: String? = nil,
managementReadyShellScript: String? = nil,
sshFallbackCommand: String,
localMoshMissingMessage: String,
localMoshUnsupportedMessage: String,
remoteMoshMissingMessage: String,
remoteMoshProbeFailedMessage: String
remoteMoshProbeFailedMessage: String,
remoteBootstrapInstallFailedMessage: String,
remoteMoshAddressFallbackMessage: String
) {
self.capabilityProbeSSHArguments = capabilityProbeSSHArguments
self.sessionSSHArguments = sessionSSHArguments
@@ -57,6 +66,7 @@ public struct MoshTerminalCommandBuilder: Sendable {
self.destination = destination
self.remoteCommandArguments = remoteCommandArguments
self.remoteRelayPort = remoteRelayPort
self.remoteIPMode = remoteIPMode
self.preparationShellScript = preparationShellScript
self.managementReadyShellScript = managementReadyShellScript
self.sshFallbackCommand = sshFallbackCommand
@@ -64,6 +74,8 @@ public struct MoshTerminalCommandBuilder: Sendable {
self.localMoshUnsupportedMessage = localMoshUnsupportedMessage
self.remoteMoshMissingMessage = remoteMoshMissingMessage
self.remoteMoshProbeFailedMessage = remoteMoshProbeFailedMessage
self.remoteBootstrapInstallFailedMessage = remoteBootstrapInstallFailedMessage
self.remoteMoshAddressFallbackMessage = remoteMoshAddressFallbackMessage
}
/// Returns a shell command that launches Mosh or falls back to SSH.
@@ -93,11 +105,19 @@ public struct MoshTerminalCommandBuilder: Sendable {
])
.map(\.remoteCommandShellQuoted)
.joined(separator: " ")
let remoteSSHConnectionScript = "printf '%s\\n' \"__CMUX_SSH_CONNECTION__${SSH_CONNECTION:-}\""
let remoteSSHConnectionCommand = "/bin/sh -c \(remoteSSHConnectionScript.remoteCommandShellQuoted)"
let remoteSSHConnectionProbe = (capabilityProbeSSHArguments + [
"-T",
destination,
remoteSSHConnectionCommand,
])
.map(\.remoteCommandShellQuoted)
.joined(separator: " ")
let moshSSHCommand = sessionSSHArguments
.map(\.remoteCommandShellQuoted)
.joined(separator: " ")
let moshArguments = ([
"--experimental-remote-ip=remote",
"--ssh=\(moshSSHCommand)",
"--server=\(remoteMoshServerResolver.remoteExecPrefixShellCommand)",
"--",
@@ -123,17 +143,6 @@ public struct MoshTerminalCommandBuilder: Sendable {
" ;;",
"esac",
"unset cmux_mosh_help",
capabilityProbe,
"cmux_mosh_probe_status=$?",
"if [ \"$cmux_mosh_probe_status\" -eq 127 ]; then",
" printf '%s\\n' \(remoteMoshMissingMessage.remoteCommandShellQuoted) >&2",
" cmux_mosh_fallback",
"fi",
"if [ \"$cmux_mosh_probe_status\" -ne 0 ]; then",
" printf '%s\\n' \(remoteMoshProbeFailedMessage.remoteCommandShellQuoted) >&2",
" cmux_mosh_fallback",
"fi",
"unset cmux_mosh_probe_status",
]
let reportsTerminalLifecycle = remoteRelayPort.map { (1...65_535).contains($0) } ?? false
if reportsTerminalLifecycle {
@@ -146,12 +155,68 @@ public struct MoshTerminalCommandBuilder: Sendable {
preparationShellScript,
"cmux_mosh_prepare_status=$?",
"if [ \"$cmux_mosh_prepare_status\" -ne 0 ]; then",
" printf '%s\\n' \(remoteMoshProbeFailedMessage.remoteCommandShellQuoted) >&2",
" printf '%s\\n' \(remoteBootstrapInstallFailedMessage.remoteCommandShellQuoted) >&2",
" cmux_mosh_fallback",
"fi",
"unset cmux_mosh_prepare_status cmux_remote_install_status",
]
}
script += [
"cmux_mosh_remote_ip_mode=\(remoteIPMode.rawValue.remoteCommandShellQuoted)",
"cmux_mosh_address_fallback=0",
]
script += [
capabilityProbe,
"cmux_mosh_probe_status=$?",
"if [ \"$cmux_mosh_probe_status\" -eq 127 ]; then",
" printf '%s\\n' \(remoteMoshMissingMessage.remoteCommandShellQuoted) >&2",
" cmux_mosh_fallback",
"fi",
"if [ \"$cmux_mosh_probe_status\" -ne 0 ]; then",
" printf '%s\\n' \(remoteMoshProbeFailedMessage.remoteCommandShellQuoted) >&2",
" cmux_mosh_fallback",
"fi",
]
if remoteIPMode == .remote {
// Mosh parses SSH_CONNECTION as four space-separated fields with
// numeric ports and uses only the server address for its UDP
// session, so validate exactly that shape and no more. When the
// advertised address is unusable (empty, malformed, wildcard, or
// loopback, which is what a port-forwarded SSH alias reports),
// fall back to Mosh's proxy resolution: unlike local mode it
// honors SSH aliases without requiring DNS on the destination.
script += [
"cmux_mosh_ssh_connection_probe_status=0",
"cmux_mosh_ssh_connection_probe=\"$(\(remoteSSHConnectionProbe) 2>/dev/null)\" || cmux_mosh_ssh_connection_probe_status=$?",
"case \"$cmux_mosh_ssh_connection_probe\" in *__CMUX_SSH_CONNECTION__*) cmux_mosh_ssh_connection=\"${cmux_mosh_ssh_connection_probe##*__CMUX_SSH_CONNECTION__}\" ;; *) cmux_mosh_ssh_connection= ;; esac",
"if [ \"$cmux_mosh_ssh_connection_probe_status\" -ne 0 ] || [ -z \"$cmux_mosh_ssh_connection\" ]; then",
" cmux_mosh_address_fallback=1",
"else",
" case \"$cmux_mosh_ssh_connection\" in",
" *' '*' '*' '*)",
" cmux_mosh_ssh_connection_tail=\"${cmux_mosh_ssh_connection#* }\"",
" cmux_mosh_ssh_peer_port=\"${cmux_mosh_ssh_connection_tail%% *}\"",
" cmux_mosh_ssh_connection_tail=\"${cmux_mosh_ssh_connection_tail#* }\"",
" cmux_mosh_ssh_server_ip=\"${cmux_mosh_ssh_connection_tail%% *}\"",
" cmux_mosh_ssh_connection_tail=\"${cmux_mosh_ssh_connection_tail#* }\"",
" cmux_mosh_ssh_server_port=\"${cmux_mosh_ssh_connection_tail%% *}\"",
" case \"$cmux_mosh_ssh_peer_port\" in ''|*[!0-9]*) cmux_mosh_address_fallback=1 ;; esac",
" case \"$cmux_mosh_ssh_server_port\" in ''|*[!0-9]*) cmux_mosh_address_fallback=1 ;; esac",
" case \"$cmux_mosh_ssh_server_ip\" in ''|0.0.0.0|::|::0|::1|127.*) cmux_mosh_address_fallback=1 ;; *[!0-9A-Fa-f:.]*) cmux_mosh_address_fallback=1 ;; esac",
" ;;",
" *) cmux_mosh_address_fallback=1 ;;",
" esac",
"fi",
"if [ \"$cmux_mosh_address_fallback\" -eq 1 ]; then cmux_mosh_remote_ip_mode=proxy; fi",
"unset cmux_mosh_ssh_connection_probe_status cmux_mosh_ssh_connection_probe cmux_mosh_ssh_connection cmux_mosh_ssh_peer_port cmux_mosh_ssh_connection_tail cmux_mosh_ssh_server_ip cmux_mosh_ssh_server_port",
]
}
script += [
"if [ \"$cmux_mosh_address_fallback\" -eq 1 ]; then",
" printf '%s\\n' \(remoteMoshAddressFallbackMessage.remoteCommandShellQuoted) >&2",
"fi",
"unset cmux_mosh_probe_status cmux_mosh_address_fallback",
]
if let managementReadyShellScript = managementReadyShellScript?
.trimmingCharacters(in: .whitespacesAndNewlines),
!managementReadyShellScript.isEmpty {
@@ -162,7 +227,7 @@ public struct MoshTerminalCommandBuilder: Sendable {
}
// Mosh exposes no reliable post-UDP-handshake callback, so this
// pre-exec launcher must not claim authoritative connected readiness.
script.append("exec \"$cmux_mosh\" \(moshArguments)")
script.append("exec \"$cmux_mosh\" \"--experimental-remote-ip=$cmux_mosh_remote_ip_mode\" \(moshArguments)")
return "/bin/sh -c \(script.joined(separator: "\n").remoteCommandShellQuoted)"
}
@@ -36,15 +36,12 @@ public struct RemoteBootstrapStagingCommandBuilder: Sendable {
}
/// Local shell code that substitutes runtime IDs and streams the bootstrap over SSH.
///
/// The SSH command is one `/bin/sh -c` remote-command string so an account's
/// configured login shell cannot parse the POSIX installer itself.
public var preparationShellScript: String {
let encodedBootstrapScript = Data(bootstrapScript.utf8).base64EncodedString()
let installCommand = ([
"/bin/sh",
"-c",
remoteInstallShellScript,
])
.map(\.remoteCommandShellQuoted)
.joined(separator: " ")
let installCommand = "/bin/sh -c \(remoteInstallShellScript.remoteCommandShellQuoted)"
let sshPrefix = installerSSHArguments
.map(\.remoteCommandShellQuoted)
.joined(separator: " ")
@@ -61,21 +58,45 @@ public struct RemoteBootstrapStagingCommandBuilder: Sendable {
"cmux_terminal_lifecycle_id_escaped=\"$(cmux_sed_escape \"$cmux_terminal_lifecycle_id\")\"",
"cmux_ssh_attempt_id_escaped=\"$(cmux_sed_escape \"$cmux_ssh_attempt_id\")\"",
"cmux_remote_bootstrap=\"$(printf '%s' \"$cmux_remote_bootstrap\" | sed \"s/__CMUX_WORKSPACE_ID__/$cmux_workspace_id_escaped/g; s/__CMUX_SURFACE_ID__/$cmux_surface_id_escaped/g; s/__CMUX_TERMINAL_LIFECYCLE_ID__/$cmux_terminal_lifecycle_id_escaped/g; s/__CMUX_SSH_ATTEMPT_ID__/$cmux_ssh_attempt_id_escaped/g\")\"",
"printf '%s' \"$cmux_remote_bootstrap\" | command \(sshPrefix) -T \(destination.remoteCommandShellQuoted) \(installCommand.remoteCommandShellQuoted)",
"cmux_remote_install_status=$?",
"unset cmux_remote_bootstrap cmux_remote_bootstrap_b64 cmux_workspace_id cmux_surface_id cmux_terminal_lifecycle_id cmux_ssh_attempt_id cmux_workspace_id_escaped cmux_surface_id_escaped cmux_terminal_lifecycle_id_escaped cmux_ssh_attempt_id_escaped",
"cmux_remote_install_stderr_file=\"$(mktemp \"${TMPDIR:-/tmp}/cmux-remote-bootstrap-install.XXXXXX\" 2>/dev/null || true)\"",
"if [ -n \"$cmux_remote_install_stderr_file\" ]; then",
" printf '%s' \"$cmux_remote_bootstrap\" | command \(sshPrefix) -T \(destination.remoteCommandShellQuoted) \(installCommand.remoteCommandShellQuoted) 2>\"$cmux_remote_install_stderr_file\"",
" cmux_remote_install_status=$?",
"else",
" printf '%s' \"$cmux_remote_bootstrap\" | command \(sshPrefix) -T \(destination.remoteCommandShellQuoted) \(installCommand.remoteCommandShellQuoted)",
" cmux_remote_install_status=$?",
"fi",
"if [ \"$cmux_remote_install_status\" -ne 0 ] && [ -n \"$cmux_remote_install_stderr_file\" ] && [ -s \"$cmux_remote_install_stderr_file\" ]; then",
" cat \"$cmux_remote_install_stderr_file\" >&2",
"fi",
"rm -f -- \"${cmux_remote_install_stderr_file:-}\" 2>/dev/null || true",
"unset cmux_remote_bootstrap cmux_remote_bootstrap_b64 cmux_workspace_id cmux_surface_id cmux_terminal_lifecycle_id cmux_ssh_attempt_id cmux_workspace_id_escaped cmux_surface_id_escaped cmux_terminal_lifecycle_id_escaped cmux_ssh_attempt_id_escaped cmux_remote_install_stderr_file",
"(exit \"$cmux_remote_install_status\")",
].joined(separator: "\n")
}
/// Small remote shell command that replaces the process with the staged bootstrap.
///
/// OpenSSH hands its remote command to the account's configured login
/// shell first. Quote the POSIX launcher as the argument to an explicit
/// `/bin/sh -c` so fish/csh/nushell never parse the staged bootstrap
/// command themselves.
public var remoteExecutionShellScript: String {
"exec /bin/sh \"$HOME/.cmux/relay/\(remoteRelayPort).bootstrap.sh\""
"/bin/sh -c \(stagedBootstrapLauncherScript.remoteCommandShellQuoted)"
}
/// Remote argv that executes the staged bootstrap.
/// Remote command argv that executes the staged bootstrap.
///
/// Mosh forwards this argv to `mosh-server`, which executes it with
/// `execvp` and no shell parsing, so the launcher must be real argv
/// elements: a single command string would be treated as a literal
/// executable pathname and fail.
public var remoteExecutionCommandArguments: [String] {
["/bin/sh", "-c", remoteExecutionShellScript]
["/bin/sh", "-c", stagedBootstrapLauncherScript]
}
private var stagedBootstrapLauncherScript: String {
"exec /bin/sh \"$HOME/.cmux/relay/\(remoteRelayPort).bootstrap.sh\""
}
private var remoteInstallShellScript: String {
@@ -37,11 +37,15 @@ public struct RemoteExecutableCommandBuilder: Sendable {
/// Returns a shell-quoted remote command that resolves and executes argv.
///
/// - Parameter arguments: Arguments forwarded to the resolved executable.
///
/// The returned value is a single `/bin/sh -c` command string. This keeps
/// resolver assignments away from a host-configured fish/csh login shell.
/// - Returns: A command suitable for an OpenSSH remote-command string.
public func remoteShellCommand(arguments: [String]) -> String {
remoteCommandArguments(arguments: arguments)
.map(\.remoteCommandShellQuoted)
.joined(separator: " ")
Self.remoteShellCommand(
script: Self.executionShellScript,
arguments: ["cmux-remote-executable", executableName, notFoundSentinel] + arguments
)
}
/// A shell command that prints the resolved executable path.
@@ -49,16 +53,10 @@ public struct RemoteExecutableCommandBuilder: Sendable {
/// The command exits 127 and emits the configured sentinel when no
/// executable can be found.
public var resolutionProbeShellCommand: String {
[
"/bin/sh",
"-c",
Self.resolutionShellScript,
"cmux-remote-executable",
executableName,
notFoundSentinel,
]
.map(\.remoteCommandShellQuoted)
.joined(separator: " ")
Self.remoteShellCommand(
script: Self.resolutionShellScript,
arguments: ["cmux-remote-executable", executableName, notFoundSentinel]
)
}
/// A command prefix to which a remote launcher may append arguments.
@@ -67,16 +65,10 @@ public struct RemoteExecutableCommandBuilder: Sendable {
/// those arguments become the resolver's argv and reach the discovered
/// server executable unchanged.
public var remoteExecPrefixShellCommand: String {
[
"/bin/sh",
"-c",
Self.executionShellScript,
"cmux-remote-executable",
executableName,
notFoundSentinel,
]
.map(\.remoteCommandShellQuoted)
.joined(separator: " ")
Self.remoteShellCommand(
script: Self.executionShellScript,
arguments: ["cmux-remote-executable", executableName, notFoundSentinel]
)
}
private static let resolutionShellScript =
@@ -102,6 +94,11 @@ public struct RemoteExecutableCommandBuilder: Sendable {
"eval \"$(/usr/libexec/path_helper -s 2>/dev/null)\"; " +
"if command -v \"$cmux_executable_name\" >/dev/null 2>&1; then " +
"cmux_executable_path=\"$(command -v \"$cmux_executable_name\")\"; fi; fi; "
private static func remoteShellCommand(script: String, arguments: [String]) -> String {
let quotedArguments = arguments.map(\.remoteCommandShellQuoted).joined(separator: " ")
return "/bin/sh -c \(script.remoteCommandShellQuoted) \(quotedArguments)"
}
}
extension String {
@@ -81,6 +81,43 @@ struct MoshTerminalCommandBuilderTests {
}
}
@Test(
"runs remote probes through POSIX sh under a fish login shell",
.enabled(if: MoshTerminalCommandBuilderTests.fishExecutablePath != nil)
)
func remoteProbeIsShellAgnostic() throws {
let fishPath = try #require(Self.fishExecutablePath)
try withFakeCommands(
sshStatus: 0,
executeRemoteCommand: true,
installRemoteMoshServerOutsidePath: true,
remoteLoginShell: fishPath
) { directory, environment in
let remoteHome = directory.appendingPathComponent("remote-home", isDirectory: true)
let staging = try #require(RemoteBootstrapStagingCommandBuilder(
installerSSHArguments: ["ssh", "-o", "RemoteCommand=none"],
destination: "[email protected]",
remoteRelayPort: 52_263,
bootstrapScript: "printf '%s\\n' fish-bootstrap"
))
let result = try run(
builder(
preparationShellScript: staging.preparationShellScript,
remoteRelayPort: 52_263
),
environment: environment
)
#expect(result.status == 0)
#expect(result.stdout.isEmpty)
#expect(result.stderr.isEmpty)
#expect(FileManager.default.fileExists(atPath: directory.appendingPathComponent("mosh.args").path))
#expect(FileManager.default.fileExists(
atPath: remoteHome.appendingPathComponent(".cmux/relay/52263.bootstrap.sh").path
))
}
}
@Test("preserves the Mosh SSH bootstrap and remote command argv")
func supportedMoshPreservesArguments() throws {
try withFakeCommands(sshStatus: 0) { directory, environment in
@@ -89,10 +126,18 @@ struct MoshTerminalCommandBuilderTests {
decoding: try Data(contentsOf: directory.appendingPathComponent("mosh.args")),
as: UTF8.self
).split(separator: "\n", omittingEmptySubsequences: false).dropLast().map(String.init)
let probeArguments = String(
let probeOutput = String(
decoding: try Data(contentsOf: directory.appendingPathComponent("ssh.args")),
as: UTF8.self
).split(separator: "\n", omittingEmptySubsequences: false).dropLast().map(String.init)
)
let probeInvocations = probeOutput
.components(separatedBy: "__CMUX_SSH_INVOCATION_END__\n")
.filter { !$0.isEmpty }
.map { $0.split(separator: "\n").map(String.init) }
let capabilityProbeArguments = try #require(
probeInvocations.first(where: { $0.last?.contains("mosh-server") == true })
)
let probeArguments = try #require(probeInvocations.first)
#expect(result.status == 0)
#expect(result.stdout.isEmpty)
@@ -100,8 +145,8 @@ struct MoshTerminalCommandBuilderTests {
#expect(Array(probeArguments.prefix(4)) == [
"-o", "RemoteCommand=none", "-T", "[email protected]",
])
#expect(probeArguments.last?.contains("mosh-server") == true)
#expect(probeArguments.last?.contains("$HOME/.local/bin") == true)
#expect(capabilityProbeArguments.last?.contains("$HOME/.local/bin") == true)
#expect(probeInvocations.contains(where: { $0.last?.contains("SSH_CONNECTION") == true }))
#expect(moshArguments[0] == "--experimental-remote-ip=remote")
#expect(moshArguments[1] == "--ssh='ssh' '-o' 'RemoteCommand=none' '-p' '2222'")
#expect(moshArguments[2].hasPrefix("--server="))
@@ -159,14 +204,177 @@ struct MoshTerminalCommandBuilderTests {
let result = try run(
builder(
sshFallbackCommand: "printf 'ssh fallback\\n'",
preparationShellScript: "false"
preparationShellScript: "printf '%s\\n' 'bootstrap install stderr' >&2; false"
),
environment: environment
)
#expect(result.status == 0)
#expect(result.stdout == "ssh fallback\n")
#expect(result.stderr == "remote probe failed\n")
#expect(result.stderr.contains("remote bootstrap install failed"))
#expect(result.stderr.contains("bootstrap install stderr"))
#expect(!result.stderr.contains("remote probe failed"))
}
}
@Test("falls back to SSH proxy address resolution when SSH advertises an unusable address")
func unusableRemoteAddressUsesProxyMode() throws {
try withFakeCommands(
sshStatus: 0,
sshConnection: "0.0.0.0 0 0.0.0.0 0"
) { directory, environment in
let result = try run(builder(), environment: environment)
let moshArguments = try String(
contentsOf: directory.appendingPathComponent("mosh.args"),
encoding: .utf8
)
#expect(result.status == 0)
#expect(moshArguments.firstLine == "--experimental-remote-ip=proxy")
#expect(result.stderr.contains("mosh address fallback engaged"))
}
}
@Test("falls back to SSH proxy address resolution when SSH_CONNECTION is empty")
func emptyRemoteAddressUsesProxyMode() throws {
try withFakeCommands(sshStatus: 0, sshConnection: "") { directory, environment in
let result = try run(builder(), environment: environment)
let moshArguments = try String(
contentsOf: directory.appendingPathComponent("mosh.args"),
encoding: .utf8
)
#expect(result.status == 0)
#expect(moshArguments.firstLine == "--experimental-remote-ip=proxy")
#expect(result.stderr.contains("mosh address fallback engaged"))
}
}
@Test("falls back to SSH proxy address resolution when SSH advertises a loopback server address")
func loopbackServerAddressUsesProxyMode() throws {
try withFakeCommands(
sshStatus: 0,
sshConnection: "127.0.0.1 51675 127.0.0.1 22"
) { directory, environment in
let result = try run(builder(), environment: environment)
let moshArguments = try String(
contentsOf: directory.appendingPathComponent("mosh.args"),
encoding: .utf8
)
#expect(result.status == 0)
#expect(moshArguments.firstLine == "--experimental-remote-ip=proxy")
#expect(result.stderr.contains("mosh address fallback engaged"))
}
}
@Test("falls back to SSH proxy address resolution when SSH_CONNECTION is truncated")
func truncatedRemoteAddressUsesProxyMode() throws {
try withFakeCommands(
sshStatus: 0,
sshConnection: "192.0.2.10 12345"
) { directory, environment in
let result = try run(builder(), environment: environment)
let moshArguments = try String(
contentsOf: directory.appendingPathComponent("mosh.args"),
encoding: .utf8
)
#expect(result.status == 0)
#expect(moshArguments.firstLine == "--experimental-remote-ip=proxy")
#expect(result.stderr.contains("mosh address fallback engaged"))
}
}
@Test("falls back to SSH proxy address resolution when a port is non-numeric")
func nonNumericPortUsesProxyMode() throws {
try withFakeCommands(
sshStatus: 0,
sshConnection: "192.0.2.10 12345 192.0.2.20 ssh"
) { directory, environment in
let result = try run(builder(), environment: environment)
let moshArguments = try String(
contentsOf: directory.appendingPathComponent("mosh.args"),
encoding: .utf8
)
#expect(result.status == 0)
#expect(moshArguments.firstLine == "--experimental-remote-ip=proxy")
#expect(result.stderr.contains("mosh address fallback engaged"))
}
}
@Test("falls back to SSH proxy address resolution when the SSH address probe fails")
func addressProbeFailureUsesProxyMode() throws {
try withFakeCommands(sshStatus: 0, sshConnectionStatus: 255) { directory, environment in
let result = try run(builder(), environment: environment)
let moshArguments = try String(
contentsOf: directory.appendingPathComponent("mosh.args"),
encoding: .utf8
)
#expect(result.status == 0)
#expect(moshArguments.firstLine == "--experimental-remote-ip=proxy")
#expect(result.stderr.contains("mosh address fallback engaged"))
}
}
@Test("keeps remote address resolution when SSH advertises zero ports")
func zeroRemotePortsKeepRemoteMode() throws {
try withFakeCommands(
sshStatus: 0,
sshConnection: "192.0.2.10 0 192.0.2.20 0"
) { directory, environment in
let result = try run(builder(), environment: environment)
let moshArguments = try String(
contentsOf: directory.appendingPathComponent("mosh.args"),
encoding: .utf8
)
#expect(result.status == 0)
#expect(moshArguments.firstLine == "--experimental-remote-ip=remote")
#expect(result.stderr.isEmpty)
}
}
@Test("keeps remote address resolution when only the peer address is unusual")
func unusualPeerAddressKeepsRemoteMode() throws {
try withFakeCommands(
sshStatus: 0,
sshConnection: "fe80::1%en0 51675 203.0.113.7 22"
) { directory, environment in
let result = try run(builder(), environment: environment)
let moshArguments = try String(
contentsOf: directory.appendingPathComponent("mosh.args"),
encoding: .utf8
)
#expect(result.status == 0)
#expect(moshArguments.firstLine == "--experimental-remote-ip=remote")
#expect(result.stderr.isEmpty)
}
}
@Test("honors an explicit local Mosh address mode")
func explicitLocalMode() throws {
try withFakeCommands(sshStatus: 0) { directory, environment in
let result = try run(
builder(remoteIPMode: .local),
environment: environment
)
let moshArguments = try String(
contentsOf: directory.appendingPathComponent("mosh.args"),
encoding: .utf8
)
#expect(result.status == 0)
#expect(moshArguments.firstLine == "--experimental-remote-ip=local")
#expect(result.stderr.isEmpty)
let probeOutput = try String(
contentsOf: directory.appendingPathComponent("ssh.args"),
encoding: .utf8
)
#expect(!probeOutput.contains("SSH_CONNECTION"))
}
}
@@ -216,6 +424,38 @@ struct MoshTerminalCommandBuilderTests {
}
}
@Test("runs the staged bootstrap through mosh-server execvp argv semantics")
func stagedBootstrapSurvivesMoshServerExec() throws {
try withFakeCommands(
sshStatus: 0,
executeRemoteCommand: true,
installRemoteMoshServerOutsidePath: true,
moshExecutesRemoteCommand: true
) { directory, environment in
let remoteHome = directory.appendingPathComponent("remote-home", isDirectory: true)
let staging = try #require(RemoteBootstrapStagingCommandBuilder(
installerSSHArguments: ["ssh", "-o", "RemoteCommand=none"],
destination: "[email protected]",
remoteRelayPort: 52_264,
bootstrapScript: "printf ran > \"$HOME/bootstrap-ran\""
))
let result = try run(
builder(
preparationShellScript: staging.preparationShellScript,
remoteRelayPort: 52_264,
remoteCommandArguments: staging.remoteExecutionCommandArguments
),
environment: environment
)
#expect(result.status == 0, "stderr: \(result.stderr)")
#expect(result.stderr.isEmpty)
#expect(FileManager.default.fileExists(
atPath: remoteHome.appendingPathComponent("bootstrap-ran").path
))
}
}
@Test("does not report connected before the Mosh transport establishes")
func failedMoshDoesNotReportConnected() throws {
try withFakeCommands(sshStatus: 0, moshStatus: 71) { directory, environment in
@@ -243,32 +483,49 @@ struct MoshTerminalCommandBuilderTests {
preparationShellScript: String? = nil,
managementReadyShellScript: String? = nil,
remoteRelayPort: Int? = nil,
localMoshExecutableName: String = "mosh"
remoteIPMode: MoshRemoteIPMode = .remote,
localMoshExecutableName: String = "mosh",
remoteCommandArguments: [String] = ["command", "space arg", "quote'arg"]
) -> MoshTerminalCommandBuilder {
MoshTerminalCommandBuilder(
capabilityProbeSSHArguments: ["ssh", "-o", "RemoteCommand=none"],
sessionSSHArguments: ["ssh", "-o", "RemoteCommand=none", "-p", "2222"],
localMoshExecutableName: localMoshExecutableName,
destination: "[email protected]",
remoteCommandArguments: ["command", "space arg", "quote'arg"],
remoteCommandArguments: remoteCommandArguments,
remoteRelayPort: remoteRelayPort,
remoteIPMode: remoteIPMode,
preparationShellScript: preparationShellScript,
managementReadyShellScript: managementReadyShellScript,
sshFallbackCommand: sshFallbackCommand,
localMoshMissingMessage: "local mosh missing",
localMoshUnsupportedMessage: "local mosh unsupported",
remoteMoshMissingMessage: "remote mosh missing",
remoteMoshProbeFailedMessage: "remote probe failed"
remoteMoshProbeFailedMessage: "remote probe failed",
remoteBootstrapInstallFailedMessage: "remote bootstrap install failed",
remoteMoshAddressFallbackMessage: "mosh address fallback engaged"
)
}
private static var fishExecutablePath: String? {
[
"/opt/homebrew/bin/fish",
"/usr/local/bin/fish",
"/usr/bin/fish",
].first(where: { FileManager.default.isExecutableFile(atPath: $0) })
}
private func withFakeCommands(
sshStatus: Int32,
installMosh: Bool = true,
moshSupportsRemoteIP: Bool = true,
executeRemoteCommand: Bool = false,
installRemoteMoshServerOutsidePath: Bool = false,
moshExecutesRemoteCommand: Bool = false,
requireManagementReady: Bool = false,
sshConnection: String? = nil,
sshConnectionStatus: Int32? = nil,
remoteLoginShell: String = "/bin/sh",
moshStatus: Int32 = 0,
operation: (URL, [String: String]) throws -> Void
) throws {
@@ -281,13 +538,22 @@ struct MoshTerminalCommandBuilderTests {
named: "ssh",
script: """
#!/bin/sh
printf '%s\\n' "$@" > "$SSH_ARGS_FILE"
printf '%s\\n' "$@" >> "$SSH_ARGS_FILE"
printf '%s\\n' '__CMUX_SSH_INVOCATION_END__' >> "$SSH_ARGS_FILE"
cmux_remote_command=
for cmux_arg in "$@"; do cmux_remote_command=$cmux_arg; done
if [ "$FAKE_SSH_EXEC_REMOTE" = "1" ]; then
cmux_remote_command=
for cmux_arg in "$@"; do cmux_remote_command=$cmux_arg; done
HOME="$FAKE_REMOTE_HOME" PATH=/usr/bin:/bin /bin/sh -c "$cmux_remote_command"
SSH_CONNECTION="$FAKE_SSH_CONNECTION" HOME="$FAKE_REMOTE_HOME" PATH=/usr/bin:/bin "$FAKE_REMOTE_LOGIN_SHELL" -c "$cmux_remote_command"
exit $?
fi
case "$cmux_remote_command" in
*SSH_CONNECTION*)
if [ -n "${FAKE_SSH_CONNECTION_STATUS:-}" ]; then
exit "$FAKE_SSH_CONNECTION_STATUS"
fi
printf '%s\\n' "__CMUX_SSH_CONNECTION__${FAKE_SSH_CONNECTION:-}"
;;
esac
exit "$FAKE_SSH_STATUS"
""",
in: directory
@@ -316,6 +582,14 @@ struct MoshTerminalCommandBuilderTests {
exit 71
fi
printf '%s\\n' "$@" > "$MOSH_ARGS_FILE"
if [ "$FAKE_MOSH_EXECS_REMOTE_COMMAND" = "1" ]; then
while [ "$#" -gt 0 ] && [ "$1" != "--" ]; do shift; done
if [ "$#" -gt 0 ]; then shift; fi
if [ "$#" -gt 0 ]; then shift; fi
if [ "$#" -gt 0 ]; then
HOME="$FAKE_REMOTE_HOME" exec "$@"
fi
fi
exit "$FAKE_MOSH_STATUS"
""",
in: directory
@@ -335,11 +609,15 @@ struct MoshTerminalCommandBuilderTests {
)
}
try operation(directory, [
"PATH": directory.path,
"PATH": directory.path + ":/usr/bin:/bin",
"FAKE_SSH_STATUS": String(sshStatus),
"FAKE_SSH_EXEC_REMOTE": executeRemoteCommand ? "1" : "0",
"FAKE_REMOTE_HOME": remoteHome.path,
"FAKE_REMOTE_LOGIN_SHELL": remoteLoginShell,
"FAKE_SSH_CONNECTION": sshConnection ?? "192.0.2.10 12345 192.0.2.20 22",
"FAKE_SSH_CONNECTION_STATUS": sshConnectionStatus.map(String.init) ?? "",
"FAKE_MOSH_SUPPORTS_REMOTE_IP": moshSupportsRemoteIP ? "1" : "0",
"FAKE_MOSH_EXECS_REMOTE_COMMAND": moshExecutesRemoteCommand ? "1" : "0",
"FAKE_REQUIRE_MANAGEMENT_READY": requireManagementReady ? "1" : "0",
"FAKE_MOSH_STATUS": String(moshStatus),
"MANAGEMENT_READY_FILE": directory.appendingPathComponent("management.ready").path,
@@ -381,3 +659,9 @@ struct MoshTerminalCommandBuilderTests {
)
}
}
private extension String {
var firstLine: String {
split(whereSeparator: \.isNewline).first.map(String.init) ?? ""
}
}
@@ -72,6 +72,7 @@ struct RemoteBootstrapStagingCommandBuilderTests {
.split(separator: "\n")
.map(String.init)
#expect(sshArguments.allSatisfy { $0.utf8.count < 4_096 })
#expect(sshArguments.contains(where: { $0.hasPrefix("/bin/sh -c '") }))
let execution = try run(
executable: "/bin/sh",
@@ -89,6 +90,143 @@ struct RemoteBootstrapStagingCommandBuilderTests {
#expect(execution.stderr.isEmpty)
}
@Test("execution argv runs the staged bootstrap under execvp semantics")
func executionArgumentsSurviveExecvp() throws {
let directory = FileManager.default.temporaryDirectory
.appendingPathComponent("cmux-bootstrap-argv-\(UUID().uuidString)", isDirectory: true)
let remoteHome = directory.appendingPathComponent("remote-home", isDirectory: true)
let relayDirectory = remoteHome.appendingPathComponent(".cmux/relay", isDirectory: true)
try FileManager.default.createDirectory(at: relayDirectory, withIntermediateDirectories: true)
defer { try? FileManager.default.removeItem(at: directory) }
try "printf '%s\\n' argv-bootstrap\n".write(
to: relayDirectory.appendingPathComponent("52264.bootstrap.sh"),
atomically: true,
encoding: .utf8
)
let builder = try #require(RemoteBootstrapStagingCommandBuilder(
installerSSHArguments: ["ssh"],
destination: "[email protected]",
remoteRelayPort: 52_264,
bootstrapScript: "printf '%s\\n' argv-bootstrap"
))
// mosh-server executes the received command argv with execvp and no
// shell parsing, so the first element must be a real executable path.
let arguments = builder.remoteExecutionCommandArguments
let execution = try run(
executable: try #require(arguments.first),
arguments: Array(arguments.dropFirst()),
environment: [
"HOME": remoteHome.path,
"PATH": "/usr/bin:/bin",
]
)
#expect(execution.status == 0)
#expect(execution.stdout == "argv-bootstrap\n")
#expect(execution.stderr.isEmpty)
}
@Test(
"installs through POSIX sh when the remote login shell is fish",
.enabled(if: RemoteBootstrapStagingCommandBuilderTests.fishExecutablePath != nil)
)
func stagesThroughFishLoginShell() throws {
let fishPath = try #require(Self.fishExecutablePath)
let directory = FileManager.default.temporaryDirectory
.appendingPathComponent("cmux-bootstrap-fish-\(UUID().uuidString)", isDirectory: true)
let remoteHome = directory.appendingPathComponent("remote-home", isDirectory: true)
try FileManager.default.createDirectory(at: remoteHome, withIntermediateDirectories: true)
defer { try? FileManager.default.removeItem(at: directory) }
let fakeSSH = directory.appendingPathComponent("ssh")
try """
#!/bin/sh
cmux_remote_command=
for cmux_argument in "$@"; do cmux_remote_command=$cmux_argument; done
HOME="$CMUX_REMOTE_HOME" PATH=/usr/bin:/bin "$CMUX_FISH_PATH" -c "$cmux_remote_command"
""".write(to: fakeSSH, atomically: true, encoding: .utf8)
try FileManager.default.setAttributes(
[.posixPermissions: 0o755],
ofItemAtPath: fakeSSH.path
)
let builder = try #require(RemoteBootstrapStagingCommandBuilder(
installerSSHArguments: [fakeSSH.path, "-o", "RemoteCommand=none"],
destination: "[email protected]",
remoteRelayPort: 52_262,
bootstrapScript: "printf '%s\\n' fish-bootstrap"
))
let preparation = try run(
executable: "/bin/sh",
arguments: ["-c", builder.preparationShellScript],
environment: [
"PATH": "/usr/bin:/bin",
"CMUX_REMOTE_HOME": remoteHome.path,
"CMUX_FISH_PATH": fishPath,
]
)
#expect(preparation.status == 0)
#expect(preparation.stderr.isEmpty)
#expect(FileManager.default.fileExists(
atPath: remoteHome.appendingPathComponent(".cmux/relay/52262.bootstrap.sh").path
))
let execution = try run(
executable: fishPath,
arguments: ["-c", builder.remoteExecutionShellScript],
environment: [
"HOME": remoteHome.path,
"PATH": "/usr/bin:/bin",
]
)
#expect(execution.status == 0)
#expect(execution.stdout == "fish-bootstrap\n")
#expect(execution.stderr.isEmpty)
}
@Test("returns installer status and captured stderr")
func reportsInstallerFailure() throws {
let directory = FileManager.default.temporaryDirectory
.appendingPathComponent("cmux-bootstrap-failure-\(UUID().uuidString)", isDirectory: true)
try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
defer { try? FileManager.default.removeItem(at: directory) }
let fakeSSH = directory.appendingPathComponent("ssh")
try """
#!/bin/sh
cat >/dev/null
printf '%s\\n' 'remote installer stderr' >&2
exit 23
""".write(to: fakeSSH, atomically: true, encoding: .utf8)
try FileManager.default.setAttributes(
[.posixPermissions: 0o755],
ofItemAtPath: fakeSSH.path
)
let builder = try #require(RemoteBootstrapStagingCommandBuilder(
installerSSHArguments: [fakeSSH.path, "-o", "RemoteCommand=none"],
destination: "[email protected]",
remoteRelayPort: 52_263,
bootstrapScript: "true"
))
let preparation = try run(
executable: "/bin/sh",
arguments: ["-c", builder.preparationShellScript],
environment: [
"PATH": "/usr/bin:/bin",
]
)
#expect(preparation.status == 23)
#expect(preparation.stderr.contains("remote installer stderr"))
#expect(!FileManager.default.fileExists(
atPath: directory.appendingPathComponent("remote-home/.cmux/relay/52263.bootstrap.sh").path
))
}
@Test("rejects an invalid relay namespace")
func invalidRelayPort() {
#expect(RemoteBootstrapStagingCommandBuilder(
@@ -120,4 +258,12 @@ struct RemoteBootstrapStagingCommandBuilderTests {
String(decoding: standardError.fileHandleForReading.readDataToEndOfFile(), as: UTF8.self)
)
}
private static var fishExecutablePath: String? {
[
"/opt/homebrew/bin/fish",
"/usr/local/bin/fish",
"/usr/bin/fish",
].first(where: { FileManager.default.isExecutableFile(atPath: $0) })
}
}
+250
View File
@@ -54327,6 +54327,256 @@
}
}
},
"cli.ssh.mosh.bootstrapInstallFailed": {
"extractionState": "manual",
"localizations": {
"ar": {
"stringUnit": {
"state": "translated",
"value": "[cmux] فشل تثبيت التمهيد البعيد؛ جارٍ المتابعة عبر SSH."
}
},
"bs": {
"stringUnit": {
"state": "translated",
"value": "[cmux] Instalacija udaljenog pokretanja nije uspjela; nastavak putem SSH-a."
}
},
"da": {
"stringUnit": {
"state": "translated",
"value": "[cmux] Installation af fjern-bootstrap mislykkedes; fortsætter via SSH."
}
},
"de": {
"stringUnit": {
"state": "translated",
"value": "[cmux] Installation des Remote-Bootstraps fehlgeschlagen; Verbindung wird über SSH fortgesetzt."
}
},
"en": {
"stringUnit": {
"state": "translated",
"value": "[cmux] Remote bootstrap install failed; continuing over SSH."
}
},
"es": {
"stringUnit": {
"state": "translated",
"value": "[cmux] Falló la instalación del arranque remoto; se continuará mediante SSH."
}
},
"fr": {
"stringUnit": {
"state": "translated",
"value": "[cmux] Échec de linstallation du bootstrap distant ; poursuite via SSH."
}
},
"it": {
"stringUnit": {
"state": "translated",
"value": "[cmux] Installazione del bootstrap remoto non riuscita; continuazione tramite SSH."
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "[cmux] リモートブートストラップのインストールに失敗したため、SSH で接続を続行します。"
}
},
"km": {
"stringUnit": {
"state": "translated",
"value": "[cmux] ការដំឡើង bootstrap ពីចម្ងាយបានបរាជ័យ ដូច្នេះបន្តតាម SSH។"
}
},
"ko": {
"stringUnit": {
"state": "translated",
"value": "[cmux] 원격 부트스트랩 설치에 실패하여 SSH로 계속합니다."
}
},
"nb": {
"stringUnit": {
"state": "translated",
"value": "[cmux] Installasjon av ekstern bootstrap mislyktes; fortsetter via SSH."
}
},
"pl": {
"stringUnit": {
"state": "translated",
"value": "[cmux] Instalacja zdalnego bootstrapu nie powiodła się; kontynuowanie przez SSH."
}
},
"pt-BR": {
"stringUnit": {
"state": "translated",
"value": "[cmux] Falha ao instalar o bootstrap remoto; continuando por SSH."
}
},
"ru": {
"stringUnit": {
"state": "translated",
"value": "[cmux] Не удалось установить удалённый bootstrap; продолжение через SSH."
}
},
"th": {
"stringUnit": {
"state": "translated",
"value": "[cmux] ติดตั้ง bootstrap ระยะไกลไม่สำเร็จ จึงดำเนินการต่อผ่าน SSH"
}
},
"tr": {
"stringUnit": {
"state": "translated",
"value": "[cmux] Uzak bootstrap kurulamadı; SSH üzerinden devam ediliyor."
}
},
"uk": {
"stringUnit": {
"state": "translated",
"value": "[cmux] Не вдалося встановити віддалений bootstrap; продовження через SSH."
}
},
"zh-Hans": {
"stringUnit": {
"state": "translated",
"value": "[cmux] 远程引导安装失败;将继续使用 SSH。"
}
},
"zh-Hant": {
"stringUnit": {
"state": "translated",
"value": "[cmux] 遠端 bootstrap 安裝失敗;將繼續使用 SSH。"
}
}
}
},
"cli.ssh.mosh.addressFallback": {
"extractionState": "manual",
"localizations": {
"ar": {
"stringUnit": {
"state": "translated",
"value": "[cmux] أعلن SSH البعيد عن عنوان غير صالح؛ سيتم تحديد عنوان Mosh عبر اتصال SSH."
}
},
"bs": {
"stringUnit": {
"state": "translated",
"value": "[cmux] Udaljeni SSH je prijavio neupotrebljivu adresu; Mosh adresa se određuje kroz SSH vezu."
}
},
"da": {
"stringUnit": {
"state": "translated",
"value": "[cmux] Fjern-SSH annoncerede en ugyldig adresse; Mosh-adressen findes via SSH-forbindelsen."
}
},
"de": {
"stringUnit": {
"state": "translated",
"value": "[cmux] Der Remote-SSH-Dienst meldete eine unbrauchbare Adresse; die Mosh-Adresse wird über die SSH-Verbindung ermittelt."
}
},
"en": {
"stringUnit": {
"state": "translated",
"value": "[cmux] Remote SSH advertised an unusable address; resolving the Mosh address through the SSH connection."
}
},
"es": {
"stringUnit": {
"state": "translated",
"value": "[cmux] El SSH remoto anunció una dirección no utilizable; la dirección de Mosh se resolverá a través de la conexión SSH."
}
},
"fr": {
"stringUnit": {
"state": "translated",
"value": "[cmux] Le SSH distant a annoncé une adresse inutilisable ; ladresse Mosh est résolue via la connexion SSH."
}
},
"it": {
"stringUnit": {
"state": "translated",
"value": "[cmux] SSH remoto ha annunciato un indirizzo inutilizzabile; lindirizzo Mosh viene risolto tramite la connessione SSH."
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "[cmux] リモート SSH が使用できないアドレスを通知したため、SSH 接続経由で Mosh のアドレスを解決します。"
}
},
"km": {
"stringUnit": {
"state": "translated",
"value": "[cmux] SSH ពីចម្ងាយបានប្រកាសអាសយដ្ឋានដែលមិនអាចប្រើបាន ដូច្នេះដោះស្រាយអាសយដ្ឋាន Mosh តាមរយៈការតភ្ជាប់ SSH។"
}
},
"ko": {
"stringUnit": {
"state": "translated",
"value": "[cmux] 원격 SSH가 사용할 수 없는 주소를 알렸습니다. SSH 연결을 통해 Mosh 주소를 확인합니다."
}
},
"nb": {
"stringUnit": {
"state": "translated",
"value": "[cmux] Ekstern SSH oppga en ubrukelig adresse; Mosh-adressen løses via SSH-forbindelsen."
}
},
"pl": {
"stringUnit": {
"state": "translated",
"value": "[cmux] Zdalny SSH ogłosił nieużyteczny adres; adres Mosh jest ustalany przez połączenie SSH."
}
},
"pt-BR": {
"stringUnit": {
"state": "translated",
"value": "[cmux] O SSH remoto anunciou um endereço inutilizável; resolvendo o endereço do Mosh pela conexão SSH."
}
},
"ru": {
"stringUnit": {
"state": "translated",
"value": "[cmux] Удалённый SSH сообщил непригодный адрес; адрес Mosh определяется через SSH-подключение."
}
},
"th": {
"stringUnit": {
"state": "translated",
"value": "[cmux] SSH ระยะไกลแจ้งที่อยู่ที่ใช้ไม่ได้ จึงค้นหาที่อยู่ Mosh ผ่านการเชื่อมต่อ SSH"
}
},
"tr": {
"stringUnit": {
"state": "translated",
"value": "[cmux] Uzak SSH kullanılamayan bir adres bildirdi; Mosh adresi SSH bağlantısı üzerinden çözümleniyor."
}
},
"uk": {
"stringUnit": {
"state": "translated",
"value": "[cmux] Віддалений SSH повідомив непридатну адресу; адреса Mosh визначається через SSH-з’єднання."
}
},
"zh-Hans": {
"stringUnit": {
"state": "translated",
"value": "[cmux] 远程 SSH 宣布的地址不可用;将通过 SSH 连接解析 Mosh 地址。"
}
},
"zh-Hant": {
"stringUnit": {
"state": "translated",
"value": "[cmux] 遠端 SSH 回報的位址無法使用;將透過 SSH 連線解析 Mosh 位址。"
}
}
}
},
"cli.ssh.mosh.remoteMissing": {
"extractionState": "manual",
"localizations": {
@@ -307,12 +307,17 @@ extension SessionRemoteWorkspaceSnapshot {
let sshInvocation = terminalArguments
.map(Self.shellQuote)
.joined(separator: " ")
let remoteCommandTemplate = [
let remoteCommandScript = [
restoredSSHTerminalConnectedShellScript(
remoteRelayPort: remoteRelayPort
),
staging.remoteExecutionShellScript,
].joined(separator: "\n")
// OpenSSH gives the command after the destination to the account's
// configured login shell. Keep fish/csh/nushell from parsing the
// POSIX lifecycle/relay script by making /bin/sh the command's
// outermost interpreter explicitly.
let remoteCommandTemplate = "/bin/sh -c \(Self.shellQuote(remoteCommandScript))"
let script = [
"cmux_restore_fail() { \(failureScript); }",
"cmux_restore_cli=\"${CMUX_BUNDLED_CLI_PATH:-}\"",
@@ -431,6 +436,14 @@ extension SessionRemoteWorkspaceSnapshot {
remoteMoshProbeFailedMessage: String(
localized: "cli.ssh.mosh.probeFailed",
defaultValue: "[cmux] Could not verify remote Mosh support; continuing over SSH."
),
remoteBootstrapInstallFailedMessage: String(
localized: "cli.ssh.mosh.bootstrapInstallFailed",
defaultValue: "[cmux] Remote bootstrap install failed; continuing over SSH."
),
remoteMoshAddressFallbackMessage: String(
localized: "cli.ssh.mosh.addressFallback",
defaultValue: "[cmux] Remote SSH advertised an unusable address; resolving the Mosh address through the SSH connection."
)
).command()
}
@@ -49,7 +49,9 @@ struct SessionRemoteWorkspaceMoshRestoreTests {
localMoshMissingMessage: "local Mosh missing",
localMoshUnsupportedMessage: "local Mosh unsupported",
remoteMoshMissingMessage: "remote Mosh missing",
remoteMoshProbeFailedMessage: "remote Mosh probe failed"
remoteMoshProbeFailedMessage: "remote Mosh probe failed",
remoteBootstrapInstallFailedMessage: "remote bootstrap install failed",
remoteMoshAddressFallbackMessage: "remote Mosh address fallback"
).command()
let process = Process()
process.executableURL = URL(fileURLWithPath: "/bin/sh")
@@ -84,7 +86,7 @@ struct SessionRemoteWorkspaceMoshRestoreTests {
let command = try #require(configuration.terminalStartupCommand)
#expect(configuration.terminalTransport == .mosh)
#expect(command.contains("--experimental-remote-ip=remote"), "\(command)")
#expect(command.contains("cmux_mosh_remote_ip_mode"), "\(command)")
#expect(command.contains("[email protected]"), "\(command)")
#expect(command.contains("2222"), "\(command)")
#expect(command.contains("ProxyJump=bastion"), "\(command)")
+1 -1
View File
@@ -35,7 +35,7 @@ This is a **living implementation spec** (also called an **execution spec**): a
- `DONE` `cmux mosh <destination>` is a command-level alias for the same first-class remote-workspace path, with Mosh selected by default.
- `DONE` `cmux mosh-tmux <destination> [--session <name>]` creates or attaches a terminal-hosted tmux session over Mosh while preserving remote metadata, daemon/control, relay, proxy/egress, upload, and reconnect behavior. The typed shell-or-tmux terminal profile persists across workspace reconnect and app session restore.
- `DONE` terminal transport is separate from management transport. Mosh carries only the interactive PTY; SSH remains responsible for `cmuxd-remote` upload/bootstrap, daemon RPC, the reverse CLI relay, proxy/egress traffic, file uploads, capability probes, and reconnect controls.
- `DONE` cmux requires a local Mosh client with `--experimental-remote-ip=remote` support (Mosh 1.4+), then checks for remote `mosh-server`. A missing/incompatible client, missing server, or failed capability probe produces an explicit message and falls back to the existing SSH terminal command.
- `DONE` cmux requires a local Mosh client with `--experimental-remote-ip` support (Mosh 1.4+), stages the cmux bootstrap, and probes for an already-installed remote `mosh-server` through an explicit POSIX `/bin/sh` management lane (cmux does not install Mosh itself). When `SSH_CONNECTION` is empty or unusable, the launcher selects Mosh's SSH-proxy address-resolution mode and explains the fallback. A missing/incompatible client, bootstrap-install failure, missing server, or failed capability probe produces a stage-specific message and falls back to the existing SSH terminal command.
- `DEFERRED` Native `ssh-tmux`-style mirroring over Mosh: tmux control mode requires a lossless byte stream, while Mosh exposes synchronized terminal screen state. `mosh-tmux` therefore provides a real roaming terminal attach, not a mislabeled native mirror. Also deferred: running the daemon/control lane over Mosh and automatic recovery from blocked UDP after a successful Mosh capability probe.
### 3.2 Bootstrap + Daemon