mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-12 07:20:45 +00:00
macos: add mode-toggle remote token sync coverage
This commit is contained in:
committed by
Nimrod Gutman
parent
bd0e6a6efd
commit
37e0b01684
@@ -508,6 +508,66 @@ final class AppState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 desiredMode: String? = switch connectionMode {
|
||||||
|
case .local:
|
||||||
|
"local"
|
||||||
|
case .remote:
|
||||||
|
"remote"
|
||||||
|
case .unconfigured:
|
||||||
|
nil
|
||||||
|
}
|
||||||
|
|
||||||
|
let currentMode = (gateway["mode"] as? String)?.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
|
if let desiredMode {
|
||||||
|
if currentMode != desiredMode {
|
||||||
|
gateway["mode"] = desiredMode
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
} else if currentMode != nil {
|
||||||
|
gateway.removeValue(forKey: "mode")
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if connectionMode == .remote {
|
||||||
|
let remoteHost = CommandResolver.parseSSHTarget(remoteTarget)?.host
|
||||||
|
let currentRemote = gateway["remote"] as? [String: Any] ?? [:]
|
||||||
|
let updated = Self.updatedRemoteGatewayConfig(
|
||||||
|
current: currentRemote,
|
||||||
|
transport: remoteTransport,
|
||||||
|
remoteUrl: remoteUrl,
|
||||||
|
remoteHost: remoteHost,
|
||||||
|
remoteTarget: remoteTarget,
|
||||||
|
remoteIdentity: remoteIdentity,
|
||||||
|
remoteToken: remoteToken)
|
||||||
|
if updated.changed {
|
||||||
|
gateway["remote"] = updated.remote
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
guard changed else { return (currentRoot, false) }
|
||||||
|
|
||||||
|
if gateway.isEmpty {
|
||||||
|
root.removeValue(forKey: "gateway")
|
||||||
|
} else {
|
||||||
|
root["gateway"] = gateway
|
||||||
|
}
|
||||||
|
return (root, true)
|
||||||
|
}
|
||||||
|
|
||||||
private func syncGatewayConfigIfNeeded() {
|
private func syncGatewayConfigIfNeeded() {
|
||||||
guard !self.isPreview, !self.isInitializing else { return }
|
guard !self.isPreview, !self.isInitializing else { return }
|
||||||
|
|
||||||
@@ -517,58 +577,19 @@ final class AppState {
|
|||||||
let remoteTransport = self.remoteTransport
|
let remoteTransport = self.remoteTransport
|
||||||
let remoteUrl = self.remoteUrl
|
let remoteUrl = self.remoteUrl
|
||||||
let remoteToken = self.remoteToken
|
let remoteToken = self.remoteToken
|
||||||
let desiredMode: String? = switch connectionMode {
|
|
||||||
case .local:
|
|
||||||
"local"
|
|
||||||
case .remote:
|
|
||||||
"remote"
|
|
||||||
case .unconfigured:
|
|
||||||
nil
|
|
||||||
}
|
|
||||||
let remoteHost = connectionMode == .remote
|
|
||||||
? CommandResolver.parseSSHTarget(remoteTarget)?.host
|
|
||||||
: nil
|
|
||||||
|
|
||||||
Task { @MainActor in
|
Task { @MainActor in
|
||||||
// Keep app-only connection settings local to avoid overwriting remote gateway config.
|
// Keep app-only connection settings local to avoid overwriting remote gateway config.
|
||||||
var root = OpenClawConfigFile.loadDict()
|
let synced = Self.syncedGatewayRoot(
|
||||||
var gateway = root["gateway"] as? [String: Any] ?? [:]
|
currentRoot: OpenClawConfigFile.loadDict(),
|
||||||
var changed = false
|
connectionMode: connectionMode,
|
||||||
|
remoteTransport: remoteTransport,
|
||||||
let currentMode = (gateway["mode"] as? String)?.trimmingCharacters(in: .whitespacesAndNewlines)
|
remoteTarget: remoteTarget,
|
||||||
if let desiredMode {
|
remoteIdentity: remoteIdentity,
|
||||||
if currentMode != desiredMode {
|
remoteUrl: remoteUrl,
|
||||||
gateway["mode"] = desiredMode
|
remoteToken: remoteToken)
|
||||||
changed = true
|
guard synced.changed else { return }
|
||||||
}
|
OpenClawConfigFile.saveDict(synced.root)
|
||||||
} else if currentMode != nil {
|
|
||||||
gateway.removeValue(forKey: "mode")
|
|
||||||
changed = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if connectionMode == .remote {
|
|
||||||
let currentRemote = gateway["remote"] as? [String: Any] ?? [:]
|
|
||||||
let updated = Self.updatedRemoteGatewayConfig(
|
|
||||||
current: currentRemote,
|
|
||||||
transport: remoteTransport,
|
|
||||||
remoteUrl: remoteUrl,
|
|
||||||
remoteHost: remoteHost,
|
|
||||||
remoteTarget: remoteTarget,
|
|
||||||
remoteIdentity: remoteIdentity,
|
|
||||||
remoteToken: remoteToken)
|
|
||||||
if updated.changed {
|
|
||||||
gateway["remote"] = updated.remote
|
|
||||||
changed = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
guard changed else { return }
|
|
||||||
if gateway.isEmpty {
|
|
||||||
root.removeValue(forKey: "gateway")
|
|
||||||
} else {
|
|
||||||
root["gateway"] = gateway
|
|
||||||
}
|
|
||||||
OpenClawConfigFile.saveDict(root)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -740,6 +761,25 @@ extension AppState {
|
|||||||
remoteIdentity: remoteIdentity,
|
remoteIdentity: remoteIdentity,
|
||||||
remoteToken: remoteToken).remote
|
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
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -31,4 +31,44 @@ struct AppStateRemoteConfigTests {
|
|||||||
|
|
||||||
#expect((remote["token"] as? String) == nil)
|
#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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user