fix: resolve macOS config store concurrency
This commit is contained in:
@@ -3,10 +3,10 @@ import Foundation
|
|||||||
enum ConfigStore {
|
enum ConfigStore {
|
||||||
struct Overrides: Sendable {
|
struct Overrides: Sendable {
|
||||||
var isRemoteMode: (@Sendable () async -> Bool)?
|
var isRemoteMode: (@Sendable () async -> Bool)?
|
||||||
var loadLocal: (@Sendable () -> [String: Any])?
|
var loadLocal: (@MainActor @Sendable () -> [String: Any])?
|
||||||
var saveLocal: (@Sendable ([String: Any]) -> Void)?
|
var saveLocal: (@MainActor @Sendable ([String: Any]) -> Void)?
|
||||||
var loadRemote: (@Sendable () async -> [String: Any])?
|
var loadRemote: (@MainActor @Sendable () async -> [String: Any])?
|
||||||
var saveRemote: (@Sendable ([String: Any]) async throws -> Void)?
|
var saveRemote: (@MainActor @Sendable ([String: Any]) async throws -> Void)?
|
||||||
}
|
}
|
||||||
|
|
||||||
private actor OverrideStore {
|
private actor OverrideStore {
|
||||||
@@ -24,9 +24,10 @@ enum ConfigStore {
|
|||||||
if let override = overrides.isRemoteMode {
|
if let override = overrides.isRemoteMode {
|
||||||
return await override()
|
return await override()
|
||||||
}
|
}
|
||||||
await MainActor.run { AppStateStore.shared.connectionMode == .remote }
|
return await MainActor.run { AppStateStore.shared.connectionMode == .remote }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@MainActor
|
||||||
static func load() async -> [String: Any] {
|
static func load() async -> [String: Any] {
|
||||||
let overrides = await self.overrideStore.overrides
|
let overrides = await self.overrideStore.overrides
|
||||||
if await self.isRemoteMode() {
|
if await self.isRemoteMode() {
|
||||||
@@ -41,6 +42,7 @@ enum ConfigStore {
|
|||||||
return ClawdisConfigFile.loadDict()
|
return ClawdisConfigFile.loadDict()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@MainActor
|
||||||
static func save(_ root: [String: Any]) async throws {
|
static func save(_ root: [String: Any]) async throws {
|
||||||
let overrides = await self.overrideStore.overrides
|
let overrides = await self.overrideStore.overrides
|
||||||
if await self.isRemoteMode() {
|
if await self.isRemoteMode() {
|
||||||
@@ -58,6 +60,7 @@ enum ConfigStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@MainActor
|
||||||
private static func loadFromGateway() async -> [String: Any] {
|
private static func loadFromGateway() async -> [String: Any] {
|
||||||
do {
|
do {
|
||||||
let snap: ConfigSnapshot = try await GatewayConnection.shared.requestDecoded(
|
let snap: ConfigSnapshot = try await GatewayConnection.shared.requestDecoded(
|
||||||
@@ -70,6 +73,7 @@ enum ConfigStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@MainActor
|
||||||
private static func saveToGateway(_ root: [String: Any]) async throws {
|
private static func saveToGateway(_ root: [String: Any]) async throws {
|
||||||
let data = try JSONSerialization.data(withJSONObject: root, options: [.prettyPrinted, .sortedKeys])
|
let data = try JSONSerialization.data(withJSONObject: root, options: [.prettyPrinted, .sortedKeys])
|
||||||
guard let raw = String(data: data, encoding: .utf8) else {
|
guard let raw = String(data: data, encoding: .utf8) else {
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ extension OnboardingView {
|
|||||||
return agent?["workspace"] as? String
|
return agent?["workspace"] as? String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@discardableResult
|
||||||
func saveAgentWorkspace(_ workspace: String?) async -> Bool {
|
func saveAgentWorkspace(_ workspace: String?) async -> Bool {
|
||||||
var root = await ConfigStore.load()
|
var root = await ConfigStore.load()
|
||||||
var agent = root["agent"] as? [String: Any] ?? [:]
|
var agent = root["agent"] as? [String: Any] ?? [:]
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ struct SettingsRootView: View {
|
|||||||
private let permissionMonitor = PermissionMonitor.shared
|
private let permissionMonitor = PermissionMonitor.shared
|
||||||
@State private var monitoringPermissions = false
|
@State private var monitoringPermissions = false
|
||||||
@State private var selectedTab: SettingsTab = .general
|
@State private var selectedTab: SettingsTab = .general
|
||||||
|
@State private var snapshotPaths: (configPath: String?, stateDir: String?) = (nil, nil)
|
||||||
let updater: UpdaterProviding?
|
let updater: UpdaterProviding?
|
||||||
private let isPreview = ProcessInfo.processInfo.isPreview
|
private let isPreview = ProcessInfo.processInfo.isPreview
|
||||||
private let isNixMode = ProcessInfo.processInfo.isNixMode
|
private let isNixMode = ProcessInfo.processInfo.isNixMode
|
||||||
@@ -102,13 +103,16 @@ struct SettingsRootView: View {
|
|||||||
guard !self.isPreview else { return }
|
guard !self.isPreview else { return }
|
||||||
await self.refreshPerms()
|
await self.refreshPerms()
|
||||||
}
|
}
|
||||||
|
.task(id: self.state.connectionMode) {
|
||||||
|
guard !self.isPreview else { return }
|
||||||
|
await self.refreshSnapshotPaths()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var nixManagedBanner: some View {
|
private var nixManagedBanner: some View {
|
||||||
// Prefer gateway-resolved paths; fall back to local env defaults if disconnected.
|
// Prefer gateway-resolved paths; fall back to local env defaults if disconnected.
|
||||||
let snapshotPaths = GatewayConnection.shared.snapshotPaths()
|
let configPath = self.snapshotPaths.configPath ?? ClawdisPaths.configURL.path
|
||||||
let configPath = snapshotPaths.configPath ?? ClawdisPaths.configURL.path
|
let stateDir = self.snapshotPaths.stateDir ?? ClawdisPaths.stateDirURL.path
|
||||||
let stateDir = snapshotPaths.stateDir ?? ClawdisPaths.stateDirURL.path
|
|
||||||
|
|
||||||
return VStack(alignment: .leading, spacing: 6) {
|
return VStack(alignment: .leading, spacing: 6) {
|
||||||
HStack(spacing: 8) {
|
HStack(spacing: 8) {
|
||||||
@@ -140,6 +144,12 @@ struct SettingsRootView: View {
|
|||||||
return requested
|
return requested
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
private func refreshSnapshotPaths() async {
|
||||||
|
let paths = await GatewayConnection.shared.snapshotPaths()
|
||||||
|
self.snapshotPaths = paths
|
||||||
|
}
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
private func refreshPerms() async {
|
private func refreshPerms() async {
|
||||||
guard !self.isPreview else { return }
|
guard !self.isPreview else { return }
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
700c6959d0a4aa855933f5e9c0d4894a07e2cfd4fa4b4aca181a078d61104704
|
13cc362f2bc44e2a05a6da5e5ba66ea602755f18ed82b18cf244c8044aa84c36
|
||||||
|
|||||||
Reference in New Issue
Block a user