macos: add mode-toggle remote token sync coverage

This commit is contained in:
Charles Dusek
2026-03-04 10:13:20 -06:00
committed by Nimrod Gutman
parent bd0e6a6efd
commit 37e0b01684
2 changed files with 129 additions and 49 deletions

View File

@@ -508,15 +508,19 @@ final class AppState {
}
}
private func syncGatewayConfigIfNeeded() {
guard !self.isPreview, !self.isInitializing else { return }
private static func syncedGatewayRoot(
currentRoot: [String: Any],
connectionMode: ConnectionMode,
remoteTransport: RemoteTransport,
remoteTarget: String,
remoteIdentity: String,
remoteUrl: String,
remoteToken: String) -> (root: [String: Any], changed: Bool)
{
var root = currentRoot
var gateway = root["gateway"] as? [String: Any] ?? [:]
var changed = false
let connectionMode = self.connectionMode
let remoteTarget = self.remoteTarget
let remoteIdentity = self.remoteIdentity
let remoteTransport = self.remoteTransport
let remoteUrl = self.remoteUrl
let remoteToken = self.remoteToken
let desiredMode: String? = switch connectionMode {
case .local:
"local"
@@ -525,15 +529,6 @@ final class AppState {
case .unconfigured:
nil
}
let remoteHost = connectionMode == .remote
? CommandResolver.parseSSHTarget(remoteTarget)?.host
: nil
Task { @MainActor in
// Keep app-only connection settings local to avoid overwriting remote gateway config.
var root = OpenClawConfigFile.loadDict()
var gateway = root["gateway"] as? [String: Any] ?? [:]
var changed = false
let currentMode = (gateway["mode"] as? String)?.trimmingCharacters(in: .whitespacesAndNewlines)
if let desiredMode {
@@ -547,6 +542,7 @@ final class AppState {
}
if connectionMode == .remote {
let remoteHost = CommandResolver.parseSSHTarget(remoteTarget)?.host
let currentRemote = gateway["remote"] as? [String: Any] ?? [:]
let updated = Self.updatedRemoteGatewayConfig(
current: currentRemote,
@@ -562,13 +558,38 @@ final class AppState {
}
}
guard changed else { return }
guard changed else { return (currentRoot, false) }
if gateway.isEmpty {
root.removeValue(forKey: "gateway")
} else {
root["gateway"] = gateway
}
OpenClawConfigFile.saveDict(root)
return (root, true)
}
private func syncGatewayConfigIfNeeded() {
guard !self.isPreview, !self.isInitializing else { return }
let connectionMode = self.connectionMode
let remoteTarget = self.remoteTarget
let remoteIdentity = self.remoteIdentity
let remoteTransport = self.remoteTransport
let remoteUrl = self.remoteUrl
let remoteToken = self.remoteToken
Task { @MainActor in
// Keep app-only connection settings local to avoid overwriting remote gateway config.
let synced = Self.syncedGatewayRoot(
currentRoot: OpenClawConfigFile.loadDict(),
connectionMode: connectionMode,
remoteTransport: remoteTransport,
remoteTarget: remoteTarget,
remoteIdentity: remoteIdentity,
remoteUrl: remoteUrl,
remoteToken: remoteToken)
guard synced.changed else { return }
OpenClawConfigFile.saveDict(synced.root)
}
}
@@ -740,6 +761,25 @@ extension AppState {
remoteIdentity: remoteIdentity,
remoteToken: remoteToken).remote
}
static func _testSyncedGatewayRoot(
currentRoot: [String: Any],
connectionMode: ConnectionMode,
remoteTransport: RemoteTransport,
remoteTarget: String,
remoteIdentity: String,
remoteUrl: String,
remoteToken: String) -> [String: Any]
{
Self.syncedGatewayRoot(
currentRoot: currentRoot,
connectionMode: connectionMode,
remoteTransport: remoteTransport,
remoteTarget: remoteTarget,
remoteIdentity: remoteIdentity,
remoteUrl: remoteUrl,
remoteToken: remoteToken).root
}
}
#endif

View File

@@ -31,4 +31,44 @@ struct AppStateRemoteConfigTests {
#expect((remote["token"] as? String) == nil)
}
@Test
func syncedGatewayRootPreservesTokenAcrossModeToggleAndClearsOnBlankRemoteToken() {
let remoteRoot = AppState._testSyncedGatewayRoot(
currentRoot: [:],
connectionMode: .remote,
remoteTransport: .direct,
remoteTarget: "",
remoteIdentity: "",
remoteUrl: "wss://gateway.example",
remoteToken: " persisted-token ")
let remoteGateway = remoteRoot["gateway"] as? [String: Any]
let remoteConfig = remoteGateway?["remote"] as? [String: Any]
#expect(remoteGateway?["mode"] as? String == "remote")
#expect(remoteConfig?["token"] as? String == "persisted-token")
let localRoot = AppState._testSyncedGatewayRoot(
currentRoot: remoteRoot,
connectionMode: .local,
remoteTransport: .direct,
remoteTarget: "",
remoteIdentity: "",
remoteUrl: "",
remoteToken: "")
let localGateway = localRoot["gateway"] as? [String: Any]
let localRemoteConfig = localGateway?["remote"] as? [String: Any]
#expect(localGateway?["mode"] as? String == "local")
#expect(localRemoteConfig?["token"] as? String == "persisted-token")
let clearedRoot = AppState._testSyncedGatewayRoot(
currentRoot: localRoot,
connectionMode: .remote,
remoteTransport: .direct,
remoteTarget: "",
remoteIdentity: "",
remoteUrl: "wss://gateway.example",
remoteToken: " ")
let clearedRemote = (clearedRoot["gateway"] as? [String: Any])?["remote"] as? [String: Any]
#expect((clearedRemote?["token"] as? String) == nil)
}
}