mirror of
https://github.com/moltbot/moltbot.git
synced 2026-03-21 16:41:56 +00:00
fix: clear stale remote discovery endpoints (#21618) (thanks @bmendonca3)
This commit is contained in:
@@ -2,6 +2,17 @@ import Foundation
|
||||
import OpenClawDiscovery
|
||||
|
||||
enum GatewayDiscoveryHelpers {
|
||||
static func resolvedServiceHost(
|
||||
for gateway: GatewayDiscoveryModel.DiscoveredGateway) -> String?
|
||||
{
|
||||
self.resolvedServiceHost(gateway.serviceHost)
|
||||
}
|
||||
|
||||
static func resolvedServiceHost(_ host: String?) -> String? {
|
||||
guard let host = self.trimmed(host), !host.isEmpty else { return nil }
|
||||
return host
|
||||
}
|
||||
|
||||
static func serviceEndpoint(
|
||||
for gateway: GatewayDiscoveryModel.DiscoveredGateway) -> (host: String, port: Int)?
|
||||
{
|
||||
@@ -12,15 +23,15 @@ enum GatewayDiscoveryHelpers {
|
||||
serviceHost: String?,
|
||||
servicePort: Int?) -> (host: String, port: Int)?
|
||||
{
|
||||
guard let host = self.trimmed(serviceHost), !host.isEmpty else { return nil }
|
||||
guard let host = self.resolvedServiceHost(serviceHost) else { return nil }
|
||||
guard let port = servicePort, port > 0, port <= 65535 else { return nil }
|
||||
return (host, port)
|
||||
}
|
||||
|
||||
static func sshTarget(for gateway: GatewayDiscoveryModel.DiscoveredGateway) -> String? {
|
||||
guard let endpoint = self.serviceEndpoint(for: gateway) else { return nil }
|
||||
guard let host = self.resolvedServiceHost(for: gateway) else { return nil }
|
||||
let user = NSUserName()
|
||||
var target = "\(user)@\(endpoint.host)"
|
||||
var target = "\(user)@\(host)"
|
||||
if gateway.sshPort != 22 {
|
||||
target += ":\(gateway.sshPort)"
|
||||
}
|
||||
|
||||
@@ -676,16 +676,16 @@ extension GeneralSettings {
|
||||
MacNodeModeCoordinator.shared.setPreferredGatewayStableID(gateway.stableID)
|
||||
|
||||
if self.state.remoteTransport == .direct {
|
||||
if let url = GatewayDiscoveryHelpers.directUrl(for: gateway) {
|
||||
self.state.remoteUrl = url
|
||||
}
|
||||
} else if let target = GatewayDiscoveryHelpers.sshTarget(for: gateway) {
|
||||
self.state.remoteTarget = target
|
||||
self.state.remoteUrl = GatewayDiscoveryHelpers.directUrl(for: gateway) ?? ""
|
||||
} else {
|
||||
self.state.remoteTarget = GatewayDiscoveryHelpers.sshTarget(for: gateway) ?? ""
|
||||
}
|
||||
if let endpoint = GatewayDiscoveryHelpers.serviceEndpoint(for: gateway) {
|
||||
OpenClawConfigFile.setRemoteGatewayUrl(
|
||||
host: endpoint.host,
|
||||
port: endpoint.port)
|
||||
} else {
|
||||
OpenClawConfigFile.clearRemoteGatewayUrl()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,16 +26,16 @@ extension OnboardingView {
|
||||
GatewayDiscoveryPreferences.setPreferredStableID(gateway.stableID)
|
||||
|
||||
if self.state.remoteTransport == .direct {
|
||||
if let url = GatewayDiscoveryHelpers.directUrl(for: gateway) {
|
||||
self.state.remoteUrl = url
|
||||
}
|
||||
} else if let target = GatewayDiscoveryHelpers.sshTarget(for: gateway) {
|
||||
self.state.remoteTarget = target
|
||||
self.state.remoteUrl = GatewayDiscoveryHelpers.directUrl(for: gateway) ?? ""
|
||||
} else {
|
||||
self.state.remoteTarget = GatewayDiscoveryHelpers.sshTarget(for: gateway) ?? ""
|
||||
}
|
||||
if let endpoint = GatewayDiscoveryHelpers.serviceEndpoint(for: gateway) {
|
||||
OpenClawConfigFile.setRemoteGatewayUrl(
|
||||
host: endpoint.host,
|
||||
port: endpoint.port)
|
||||
} else {
|
||||
OpenClawConfigFile.clearRemoteGatewayUrl()
|
||||
}
|
||||
|
||||
self.state.connectionMode = .remote
|
||||
|
||||
@@ -223,6 +223,19 @@ enum OpenClawConfigFile {
|
||||
}
|
||||
}
|
||||
|
||||
static func clearRemoteGatewayUrl() {
|
||||
self.updateGatewayDict { gateway in
|
||||
guard var remote = gateway["remote"] as? [String: Any] else { return }
|
||||
guard remote["url"] != nil else { return }
|
||||
remote.removeValue(forKey: "url")
|
||||
if remote.isEmpty {
|
||||
gateway.removeValue(forKey: "remote")
|
||||
} else {
|
||||
gateway["remote"] = remote
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static func remoteGatewayUrl() -> URL? {
|
||||
let root = self.loadDict()
|
||||
guard let gateway = root["gateway"] as? [String: Any],
|
||||
|
||||
@@ -42,6 +42,21 @@ struct GatewayDiscoveryHelpersTests {
|
||||
#expect(parsed?.port == 2201)
|
||||
}
|
||||
|
||||
@Test func sshTargetAllowsMissingResolvedServicePort() {
|
||||
let gateway = self.makeGateway(
|
||||
serviceHost: "resolved.example.ts.net",
|
||||
servicePort: nil,
|
||||
sshPort: 2201)
|
||||
|
||||
guard let target = GatewayDiscoveryHelpers.sshTarget(for: gateway) else {
|
||||
Issue.record("expected ssh target")
|
||||
return
|
||||
}
|
||||
let parsed = CommandResolver.parseSSHTarget(target)
|
||||
#expect(parsed?.host == "resolved.example.ts.net")
|
||||
#expect(parsed?.port == 2201)
|
||||
}
|
||||
|
||||
@Test func sshTargetRejectsTxtOnlyGateways() {
|
||||
let gateway = self.makeGateway(
|
||||
serviceHost: nil,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import Foundation
|
||||
import OpenClawDiscovery
|
||||
import SwiftUI
|
||||
import Testing
|
||||
@@ -25,4 +26,36 @@ struct OnboardingViewSmokeTests {
|
||||
let order = OnboardingView.pageOrder(for: .local, showOnboardingChat: false)
|
||||
#expect(!order.contains(8))
|
||||
}
|
||||
|
||||
@Test func selectRemoteGatewayClearsStaleSshTargetWhenEndpointUnresolved() async {
|
||||
let override = FileManager().temporaryDirectory
|
||||
.appendingPathComponent("openclaw-config-\(UUID().uuidString)")
|
||||
.appendingPathComponent("openclaw.json")
|
||||
.path
|
||||
|
||||
await TestIsolation.withEnvValues(["OPENCLAW_CONFIG_PATH": override]) {
|
||||
let state = AppState(preview: true)
|
||||
state.remoteTransport = .ssh
|
||||
state.remoteTarget = "user@old-host:2222"
|
||||
let view = OnboardingView(
|
||||
state: state,
|
||||
permissionMonitor: PermissionMonitor.shared,
|
||||
discoveryModel: GatewayDiscoveryModel(localDisplayName: InstanceIdentity.displayName))
|
||||
let gateway = GatewayDiscoveryModel.DiscoveredGateway(
|
||||
displayName: "Unresolved",
|
||||
serviceHost: nil,
|
||||
servicePort: nil,
|
||||
lanHost: "txt-host.local",
|
||||
tailnetDns: "txt-host.ts.net",
|
||||
sshPort: 22,
|
||||
gatewayPort: 18789,
|
||||
cliPath: "/tmp/openclaw",
|
||||
stableID: UUID().uuidString,
|
||||
debugID: UUID().uuidString,
|
||||
isLocal: false)
|
||||
|
||||
view.selectRemoteGateway(gateway)
|
||||
#expect(state.remoteTarget.isEmpty)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +62,31 @@ struct OpenClawConfigFileTests {
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
@Test
|
||||
func clearRemoteGatewayUrlRemovesOnlyUrlField() async {
|
||||
let override = FileManager().temporaryDirectory
|
||||
.appendingPathComponent("openclaw-config-\(UUID().uuidString)")
|
||||
.appendingPathComponent("openclaw.json")
|
||||
.path
|
||||
|
||||
await TestIsolation.withEnvValues(["OPENCLAW_CONFIG_PATH": override]) {
|
||||
OpenClawConfigFile.saveDict([
|
||||
"gateway": [
|
||||
"remote": [
|
||||
"url": "wss://old-host:111",
|
||||
"token": "tok",
|
||||
],
|
||||
],
|
||||
])
|
||||
OpenClawConfigFile.clearRemoteGatewayUrl()
|
||||
let root = OpenClawConfigFile.loadDict()
|
||||
let remote = ((root["gateway"] as? [String: Any])?["remote"] as? [String: Any]) ?? [:]
|
||||
#expect((remote["url"] as? String) == nil)
|
||||
#expect((remote["token"] as? String) == "tok")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
func stateDirOverrideSetsConfigPath() async {
|
||||
let dir = FileManager().temporaryDirectory
|
||||
|
||||
Reference in New Issue
Block a user