chore: format swift/ts and fix gateway lint

This commit is contained in:
Peter Steinberger
2025-12-09 17:11:25 +00:00
parent b6bd39660f
commit 8d888b426f
18 changed files with 205 additions and 121 deletions

View File

@@ -26,11 +26,14 @@ final class AppState: ObservableObject {
}
@Published var onboardingSeen: Bool {
didSet { self.ifNotPreview { UserDefaults.standard.set(self.onboardingSeen, forKey: "clawdis.onboardingSeen") } }
didSet { self.ifNotPreview { UserDefaults.standard.set(self.onboardingSeen, forKey: "clawdis.onboardingSeen") }
}
}
@Published var debugPaneEnabled: Bool {
didSet { self.ifNotPreview { UserDefaults.standard.set(self.debugPaneEnabled, forKey: "clawdis.debugPaneEnabled") } }
didSet {
self.ifNotPreview { UserDefaults.standard.set(self.debugPaneEnabled, forKey: "clawdis.debugPaneEnabled") }
}
}
@Published var swabbleEnabled: Bool {
@@ -63,7 +66,9 @@ final class AppState: ObservableObject {
}
@Published var iconAnimationsEnabled: Bool {
didSet { self.ifNotPreview { UserDefaults.standard.set(self.iconAnimationsEnabled, forKey: iconAnimationsEnabledKey) } }
didSet { self.ifNotPreview { UserDefaults.standard.set(
self.iconAnimationsEnabled,
forKey: iconAnimationsEnabledKey) } }
}
@Published var showDockIcon: Bool {
@@ -98,19 +103,27 @@ final class AppState: ObservableObject {
}
@Published var voiceWakeAdditionalLocaleIDs: [String] {
didSet { self.ifNotPreview { UserDefaults.standard.set(self.voiceWakeAdditionalLocaleIDs, forKey: voiceWakeAdditionalLocalesKey) } }
didSet { self.ifNotPreview { UserDefaults.standard.set(
self.voiceWakeAdditionalLocaleIDs,
forKey: voiceWakeAdditionalLocalesKey) } }
}
@Published var voiceWakeForwardEnabled: Bool {
didSet { self.ifNotPreview { UserDefaults.standard.set(self.voiceWakeForwardEnabled, forKey: voiceWakeForwardEnabledKey) } }
didSet { self.ifNotPreview { UserDefaults.standard.set(
self.voiceWakeForwardEnabled,
forKey: voiceWakeForwardEnabledKey) } }
}
@Published var voiceWakeForwardCommand: String {
didSet { self.ifNotPreview { UserDefaults.standard.set(self.voiceWakeForwardCommand, forKey: voiceWakeForwardCommandKey) } }
didSet { self.ifNotPreview { UserDefaults.standard.set(
self.voiceWakeForwardCommand,
forKey: voiceWakeForwardCommandKey) } }
}
@Published var voicePushToTalkEnabled: Bool {
didSet { self.ifNotPreview { UserDefaults.standard.set(self.voicePushToTalkEnabled, forKey: voicePushToTalkEnabledKey) } }
didSet { self.ifNotPreview { UserDefaults.standard.set(
self.voicePushToTalkEnabled,
forKey: voicePushToTalkEnabledKey) } }
}
@Published var iconOverride: IconOverrideSelection {
@@ -131,7 +144,9 @@ final class AppState: ObservableObject {
}
@Published var connectionMode: ConnectionMode {
didSet { self.ifNotPreview { UserDefaults.standard.set(self.connectionMode.rawValue, forKey: connectionModeKey) } }
didSet {
self.ifNotPreview { UserDefaults.standard.set(self.connectionMode.rawValue, forKey: connectionModeKey) }
}
}
@Published var webChatEnabled: Bool {

View File

@@ -1,7 +1,7 @@
import ClawdisProtocol
import Foundation
import OSLog
import SwiftUI
import ClawdisProtocol
struct ControlHeartbeatEvent: Codable {
let ts: Double
@@ -14,7 +14,7 @@ struct ControlHeartbeatEvent: Codable {
}
struct ControlAgentEvent: Codable, Sendable, Identifiable {
var id: String { "\(runId)-\(seq)" }
var id: String { "\(self.runId)-\(self.seq)" }
let runId: String
let seq: Int
let stream: String
@@ -173,7 +173,8 @@ final class ControlChannel: ObservableObject {
if let data = evt.payload?.value,
JSONSerialization.isValidJSONObject(data),
let blob = try? JSONSerialization.data(withJSONObject: data),
let agent = try? JSONDecoder().decode(AgentEvent.self, from: blob) {
let agent = try? JSONDecoder().decode(AgentEvent.self, from: blob)
{
Task { @MainActor in
AgentEventStore.shared.append(ControlAgentEvent(
runId: agent.runid,

View File

@@ -108,7 +108,8 @@ enum DebugActionError: LocalizedError {
var errorDescription: String? {
switch self {
case let .message(text): text
case let .message(text):
text
}
}
}

View File

@@ -1,6 +1,6 @@
import ClawdisProtocol
import Foundation
import OSLog
import ClawdisProtocol
struct GatewayEvent: Codable {
let type: String
@@ -27,7 +27,7 @@ private actor GatewayChannelActor {
private var shouldReconnect = true
private var lastSeq: Int?
private var lastTick: Date?
private var tickIntervalMs: Double = 30_000
private var tickIntervalMs: Double = 30000
private let decoder = JSONDecoder()
private let encoder = JSONEncoder()
@@ -88,7 +88,8 @@ private actor GatewayChannelActor {
let type = obj["type"] as? String else { return false }
if type == "hello-ok" {
if let policy = obj["policy"] as? [String: Any],
let tick = policy["tickIntervalMs"] as? Double {
let tick = policy["tickIntervalMs"] as? Double
{
self.tickIntervalMs = tick
}
self.lastTick = Date()

View File

@@ -296,7 +296,9 @@ struct GeneralSettings: View {
.disabled(self.relayInstalling)
}
Text(self.relayInstallMessage ?? "Installs the global \"clawdis\" package and expects the gateway on port 18789.")
Text(self
.relayInstallMessage ??
"Installs the global \"clawdis\" package and expects the gateway on port 18789.")
.font(.caption)
.foregroundStyle(.secondary)
.lineLimit(2)

View File

@@ -151,15 +151,15 @@ final class HealthStore: ObservableObject {
/// Short, human-friendly detail for the last failure, used in the UI.
var detailLine: String? {
if let error = self.lastError, !error.isEmpty {
let lower = error.lowercased()
if lower.contains("connection refused") {
return "The gateway control port (127.0.0.1:18789) isnt listening — restart Clawdis to bring it back."
}
if lower.contains("timeout") {
return "Timed out waiting for the control server; the relay may be crashed or still starting."
}
return error
if let error = self.lastError, !error.isEmpty {
let lower = error.lowercased()
if lower.contains("connection refused") {
return "The gateway control port (127.0.0.1:18789) isnt listening — restart Clawdis to bring it back."
}
if lower.contains("timeout") {
return "Timed out waiting for the control server; the relay may be crashed or still starting."
}
return error
}
return nil
}

View File

@@ -1,7 +1,7 @@
import ClawdisProtocol
import Cocoa
import Foundation
import OSLog
import ClawdisProtocol
struct InstanceInfo: Identifiable, Codable {
let id: String
@@ -79,7 +79,8 @@ final class InstancesStore: ObservableObject {
case let .event(evt) where evt.event == "presence":
if let payload = evt.payload?.value as? [String: Any],
let presence = payload["presence"],
let presenceData = try? JSONSerialization.data(withJSONObject: presence) {
let presenceData = try? JSONSerialization.data(withJSONObject: presence)
{
Task { @MainActor [weak self] in self?.decodeAndApplyPresenceData(presenceData) }
}
default:
@@ -104,7 +105,8 @@ final class InstancesStore: ObservableObject {
switch frame {
case let .helloOk(hello):
if JSONSerialization.isValidJSONObject(hello.snapshot.presence),
let data = try? JSONEncoder().encode(hello.snapshot.presence) {
let data = try? JSONEncoder().encode(hello.snapshot.presence)
{
Task { @MainActor [weak self] in self?.decodeAndApplyPresenceData(data) }
}
default:
@@ -323,7 +325,7 @@ extension InstancesStore {
mode: "remote",
reason: "preview",
text: "Relay node · tunnel ok",
ts: Date().timeIntervalSince1970 * 1000 - 45_000),
ts: Date().timeIntervalSince1970 * 1000 - 45000),
]) -> InstancesStore {
let store = InstancesStore(isPreview: true)
store.instances = instances

View File

@@ -181,7 +181,8 @@ struct OnboardingView: View {
self.onboardingPage {
Text("Install the relay")
.font(.largeTitle.weight(.semibold))
Text("Clawdis now runs the WebSocket gateway from the global \"clawdis\" package. Install/update it here and well check Node for you.")
Text(
"Clawdis now runs the WebSocket gateway from the global \"clawdis\" package. Install/update it here and well check Node for you.")
.font(.body)
.foregroundStyle(.secondary)
.multilineTextAlignment(.center)

View File

@@ -132,9 +132,8 @@ struct PermissionsSettings_Previews: PreviewProvider {
.speechRecognition: false,
],
refresh: {},
showOnboarding: {}
)
.frame(width: SettingsTab.windowWidth, height: SettingsTab.windowHeight)
showOnboarding: {})
.frame(width: SettingsTab.windowWidth, height: SettingsTab.windowHeight)
}
}
#endif

View File

@@ -1,5 +1,5 @@
import Foundation
import ClawdisIPC
import Foundation
// Lightweight SemVer helper (major.minor.patch only) for relay compatibility checks.
struct Semver: Comparable, CustomStringConvertible, Sendable {

View File

@@ -126,7 +126,7 @@ extension SessionRow {
id: "global",
key: "global",
kind: .global,
updatedAt: Date().addingTimeInterval(-86_400),
updatedAt: Date().addingTimeInterval(-86400),
sessionId: nil,
thinkingLevel: nil,
verboseLevel: nil,

View File

@@ -101,7 +101,14 @@ final class WebChatWindowController: NSWindowController, WKNavigationDelegate {
private func loadWebChat(baseEndpoint: URL) {
var comps = URLComponents(url: baseEndpoint.appendingPathComponent("webchat/"), resolvingAgainstBaseURL: false)
comps?.queryItems = [URLQueryItem(name: "session", value: self.sessionKey)]
var items = [URLQueryItem(name: "session", value: self.sessionKey)]
if let hostName = Host.current().localizedName ?? Host.current().name {
items.append(URLQueryItem(name: "host", value: hostName))
}
if let ip = Self.primaryIPv4Address() {
items.append(URLQueryItem(name: "ip", value: ip))
}
comps?.queryItems = items
guard let url = comps?.url else {
self.showError("invalid webchat url")
return
@@ -197,6 +204,37 @@ final class WebChatWindowController: NSWindowController, WKNavigationDelegate {
}
}
extension WebChatWindowController {
/// Returns the first non-loopback IPv4 address, skipping link-local (169.254.x.x).
fileprivate static func primaryIPv4Address() -> String? {
var ifaddr: UnsafeMutablePointer<ifaddrs>?
guard getifaddrs(&ifaddr) == 0, let first = ifaddr else { return nil }
defer { freeifaddrs(ifaddr) }
for ptr in sequence(first: first, next: { $0.pointee.ifa_next }) {
let flags = Int32(ptr.pointee.ifa_flags)
let addrFamily = ptr.pointee.ifa_addr.pointee.sa_family
if (flags & IFF_UP) == 0 || (flags & IFF_LOOPBACK) != 0 { continue }
if addrFamily == UInt8(AF_INET) {
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
if getnameinfo(
ptr.pointee.ifa_addr,
socklen_t(ptr.pointee.ifa_addr.pointee.sa_len),
&hostname,
socklen_t(hostname.count),
nil,
0,
NI_NUMERICHOST) == 0
{
let ip = String(cString: hostname)
if !ip.hasPrefix("169.254") { return ip }
}
}
}
return nil
}
}
// MARK: - Manager
@MainActor