

























@@ -52,7 +52,11 @@ enum OpenClawConfigFile {
5252}
5353}
545455-static func saveDict(_ dict: [String: Any]) {
55+static func saveDict(
56+ _ dict: [String: Any],
57+ preserveExistingKeys: Bool = false,
58+ allowGatewayAuthMutation: Bool = false)
59+{
5660self.withFileLock {
5761 // Nix mode disables config writes in production, but tests rely on saving temp configs.
5862if ProcessInfo.processInfo.isNixMode, !ProcessInfo.processInfo.isRunningTests { return }
@@ -64,7 +68,15 @@ enum OpenClawConfigFile {
6468let hadMetaBefore = self.hasMeta(previousRoot)
6569let gatewayModeBefore = self.gatewayMode(previousRoot)
667067-var output = dict
71+var output = if preserveExistingKeys, let previousRoot {
72+self.mergeExistingConfig(previousRoot, overridingWith: dict)
73+} else {
74+ dict
75+}
76+let preservedGatewayAuth = self.preserveGatewayAuthIfNeeded(
77+ previousRoot: previousRoot,
78+ output: &output,
79+ allowGatewayAuthMutation: allowGatewayAuthMutation)
6880self.stampMeta(&output)
69817082do {
@@ -76,13 +88,16 @@ enum OpenClawConfigFile {
7688let nextBytes = data.count
7789let nextAttributes = try? FileManager().attributesOfItem(atPath: url.path)
7890let gatewayModeAfter = self.gatewayMode(output)
79-let suspicious = self.configWriteSuspiciousReasons(
91+var suspicious = self.configWriteSuspiciousReasons(
8092 existsBefore: previousData != nil,
8193 previousBytes: previousBytes,
8294 nextBytes: nextBytes,
8395 hadMetaBefore: hadMetaBefore,
8496 gatewayModeBefore: gatewayModeBefore,
8597 gatewayModeAfter: gatewayModeAfter)
98+if preservedGatewayAuth {
99+ suspicious.append("gateway-auth-preserved")
100+}
86101if !suspicious.isEmpty {
87102self.logger.warning("config write anomaly (\(suspicious.joined(separator: ", "))) at \(url.path)")
88103}
@@ -123,7 +138,7 @@ enum OpenClawConfigFile {
123138"hasMetaAfter": self.hasMeta(output),
124139"gatewayModeBefore": gatewayModeBefore ?? NSNull(),
125140"gatewayModeAfter": self.gatewayMode(output) ?? NSNull(),
126-"suspicious": [],
141+"suspicious": preservedGatewayAuth ? ["gateway-auth-preserved"] : [],
127142"error": error.localizedDescription,
128143])
129144}
@@ -331,6 +346,52 @@ enum OpenClawConfigFile {
331346return trimmed.isEmpty ? nil : trimmed
332347}
333348349+private static func gatewayAuth(_ root: [String: Any]?) -> [String: Any]? {
350+guard let root,
351+let gateway = root["gateway"] as? [String: Any]
352+else { return nil }
353+return gateway["auth"] as? [String: Any]
354+}
355+356+private static func configDictionariesEqual(_ left: [String: Any]?, _ right: [String: Any]) -> Bool {
357+guard let left else { return false }
358+return NSDictionary(dictionary: left).isEqual(NSDictionary(dictionary: right))
359+}
360+361+private static func mergeExistingConfig(
362+ _ existing: [String: Any],
363+ overridingWith next: [String: Any]) -> [String: Any]
364+{
365+var merged = existing
366+for (key, value) in next {
367+if let nextDict = value as? [String: Any],
368+let existingDict = merged[key] as? [String: Any]
369+{
370+merged[key] = self.mergeExistingConfig(existingDict, overridingWith: nextDict)
371+} else {
372+merged[key] = value
373+}
374+}
375+return merged
376+}
377+378+private static func preserveGatewayAuthIfNeeded(
379+ previousRoot: [String: Any]?,
380+ output: inout [String: Any],
381+ allowGatewayAuthMutation: Bool) -> Bool
382+{
383+guard !allowGatewayAuthMutation,
384+let previousAuth = self.gatewayAuth(previousRoot)
385+else {
386+return false
387+}
388+var gateway = output["gateway"] as? [String: Any] ?? [:]
389+let changed = !self.configDictionariesEqual(gateway["auth"] as? [String: Any], previousAuth)
390+gateway["auth"] = previousAuth
391+output["gateway"] = gateway
392+return changed
393+}
394+334395private static func configWriteSuspiciousReasons(
335396 existsBefore: Bool,
336397 previousBytes: Int?,
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。