chore: format swift/ts and fix gateway lint
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -108,7 +108,8 @@ enum DebugActionError: LocalizedError {
|
||||
|
||||
var errorDescription: String? {
|
||||
switch self {
|
||||
case let .message(text): text
|
||||
case let .message(text):
|
||||
text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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) isn’t 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) isn’t 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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 we’ll check Node for you.")
|
||||
Text(
|
||||
"Clawdis now runs the WebSocket gateway from the global \"clawdis\" package. Install/update it here and we’ll check Node for you.")
|
||||
.font(.body)
|
||||
.foregroundStyle(.secondary)
|
||||
.multilineTextAlignment(.center)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user