diff --git a/apps/macos/Sources/Clawdis/AppMain.swift b/apps/macos/Sources/Clawdis/AppMain.swift index 99e564224..093fb8bae 100644 --- a/apps/macos/Sources/Clawdis/AppMain.swift +++ b/apps/macos/Sources/Clawdis/AppMain.swift @@ -2765,6 +2765,7 @@ struct DebugSettings: View { @State private var modelsLoading = false @State private var modelsError: String? @ObservedObject private var relayManager = RelayProcessManager.shared + @State private var relayRootInput: String = RelayProcessManager.shared.projectRootPath() var body: some View { VStack(alignment: .leading, spacing: 10) { @@ -2793,6 +2794,28 @@ struct DebugSettings: View { .frame(height: 180) .overlay(RoundedRectangle(cornerRadius: 6).stroke(Color.secondary.opacity(0.2))) } + VStack(alignment: .leading, spacing: 6) { + Text("Relay project root") + .font(.caption.weight(.semibold)) + HStack(spacing: 8) { + TextField("Path to clawdis repo", text: self.$relayRootInput) + .textFieldStyle(.roundedBorder) + .font(.caption.monospaced()) + .onSubmit { self.saveRelayRoot() } + Button("Save") { self.saveRelayRoot() } + .buttonStyle(.borderedProminent) + Button("Reset") { + let def = FileManager.default.homeDirectoryForCurrentUser + .appendingPathComponent("Projects/clawdis").path + self.relayRootInput = def + self.saveRelayRoot() + } + .buttonStyle(.bordered) + } + Text("Used for pnpm/node fallback and PATH population when launching the relay.") + .font(.caption2) + .foregroundStyle(.secondary) + } LabeledContent("Model catalog") { VStack(alignment: .leading, spacing: 6) { Text(self.modelCatalogPath) @@ -2890,6 +2913,10 @@ struct DebugSettings: View { let url = Bundle.main.bundleURL NSWorkspace.shared.activateFileViewerSelecting([url]) } + + private func saveRelayRoot() { + RelayProcessManager.shared.setProjectRoot(path: self.relayRootInput) + } } struct AboutSettings: View { diff --git a/apps/macos/Sources/Clawdis/RelayProcessManager.swift b/apps/macos/Sources/Clawdis/RelayProcessManager.swift index 1119d1d13..4a8c04536 100644 --- a/apps/macos/Sources/Clawdis/RelayProcessManager.swift +++ b/apps/macos/Sources/Clawdis/RelayProcessManager.swift @@ -9,6 +9,10 @@ import Darwin final class RelayProcessManager: ObservableObject { static let shared = RelayProcessManager() + private enum Defaults { + static let projectRootPath = "clawdis.relayProjectRootPath" + } + enum Status: Equatable { case stopped case starting @@ -246,11 +250,34 @@ final class RelayProcessManager: ObservableObject { } private func defaultProjectRoot() -> URL { - let home = FileManager.default.homeDirectoryForCurrentUser - let candidate = home.appendingPathComponent("Projects/clawdis") - if FileManager.default.fileExists(atPath: candidate.path) { - return candidate + if let stored = UserDefaults.standard.string(forKey: Defaults.projectRootPath), + let url = self.expandPath(stored) { + return url } - return home + let fallback = FileManager.default.homeDirectoryForCurrentUser + .appendingPathComponent("Projects/clawdis") + if FileManager.default.fileExists(atPath: fallback.path) { + return fallback + } + return FileManager.default.homeDirectoryForCurrentUser + } + + func setProjectRoot(path: String) { + UserDefaults.standard.set(path, forKey: Defaults.projectRootPath) + } + + func projectRootPath() -> String { + UserDefaults.standard.string(forKey: Defaults.projectRootPath) + ?? FileManager.default.homeDirectoryForCurrentUser + .appendingPathComponent("Projects/clawdis").path + } + + private func expandPath(_ path: String) -> URL? { + var expanded = path + if expanded.hasPrefix("~") { + let home = FileManager.default.homeDirectoryForCurrentUser.path + expanded.replaceSubrange(expanded.startIndex...expanded.startIndex, with: home) + } + return URL(fileURLWithPath: expanded) } }