From 9387ecf0437341d4fabd1381cc475890b93bb94c Mon Sep 17 00:00:00 2001 From: jeffersonwarrior Date: Thu, 1 Jan 2026 21:26:37 -0600 Subject: [PATCH] fix(macos): support password auth mode for gateway connections MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GatewayChannel now sends both 'token' and 'password' fields in the auth payload to support both authentication modes. Gateway checks the field matching its auth.mode configuration ('token' or 'password'). Also adds config file password fallback for remote mode, allowing gateway password to be configured in ~/.clawdis/clawdis.json without requiring environment variables. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- apps/macos/Sources/Clawdis/ClawdisConfigFile.swift | 9 +++++++++ apps/macos/Sources/Clawdis/GatewayChannel.swift | 8 +++++++- apps/macos/Sources/Clawdis/GatewayEndpointStore.swift | 8 +++++++- src/gateway/hooks-mapping.ts | 4 ++-- 4 files changed, 25 insertions(+), 4 deletions(-) diff --git a/apps/macos/Sources/Clawdis/ClawdisConfigFile.swift b/apps/macos/Sources/Clawdis/ClawdisConfigFile.swift index 3a712ebce..e0b3532ad 100644 --- a/apps/macos/Sources/Clawdis/ClawdisConfigFile.swift +++ b/apps/macos/Sources/Clawdis/ClawdisConfigFile.swift @@ -97,4 +97,13 @@ enum ClawdisConfigFile { self.logger.debug("agent workspace updated set=\(!trimmed.isEmpty)") } + static func gatewayPassword() -> String? { + let root = self.loadDict() + guard let gateway = root["gateway"] as? [String: Any], + let remote = gateway["remote"] as? [String: Any] else { + return nil + } + return remote["password"] as? String + } + } diff --git a/apps/macos/Sources/Clawdis/GatewayChannel.swift b/apps/macos/Sources/Clawdis/GatewayChannel.swift index 4de00bd3e..af1c1635c 100644 --- a/apps/macos/Sources/Clawdis/GatewayChannel.swift +++ b/apps/macos/Sources/Clawdis/GatewayChannel.swift @@ -213,7 +213,13 @@ actor GatewayChannelActor { "userAgent": ProtoAnyCodable(ProcessInfo.processInfo.operatingSystemVersionString), ] if let token = self.token { - params["auth"] = ProtoAnyCodable(["token": ProtoAnyCodable(token)]) + // Send both 'token' and 'password' to support both auth modes. + // Gateway checks the field matching its auth.mode configuration. + let authDict: [String: ProtoAnyCodable] = [ + "token": ProtoAnyCodable(token), + "password": ProtoAnyCodable(token), + ] + params["auth"] = ProtoAnyCodable(authDict) } let frame = RequestFrame( diff --git a/apps/macos/Sources/Clawdis/GatewayEndpointStore.swift b/apps/macos/Sources/Clawdis/GatewayEndpointStore.swift index 192eb3838..b0eec702a 100644 --- a/apps/macos/Sources/Clawdis/GatewayEndpointStore.swift +++ b/apps/macos/Sources/Clawdis/GatewayEndpointStore.swift @@ -23,7 +23,13 @@ actor GatewayEndpointStore { static let live = Deps( mode: { await MainActor.run { AppStateStore.shared.connectionMode } }, - token: { ProcessInfo.processInfo.environment["CLAWDIS_GATEWAY_TOKEN"] }, + token: { + // First check env var, fallback to config file + if let envToken = ProcessInfo.processInfo.environment["CLAWDIS_GATEWAY_TOKEN"], !envToken.isEmpty { + return envToken + } + return ClawdisConfigFile.gatewayPassword() + }, localPort: { GatewayEnvironment.gatewayPort() }, remotePortIfRunning: { await RemoteTunnelManager.shared.controlTunnelPortIfRunning() }, ensureRemoteTunnel: { try await RemoteTunnelManager.shared.ensureControlTunnel() }) diff --git a/src/gateway/hooks-mapping.ts b/src/gateway/hooks-mapping.ts index 1d29d786b..81c001878 100644 --- a/src/gateway/hooks-mapping.ts +++ b/src/gateway/hooks-mapping.ts @@ -18,7 +18,7 @@ export type HookMappingResolved = { messageTemplate?: string; textTemplate?: string; deliver?: boolean; - channel?: "last" | "whatsapp" | "telegram" | "discord"; + channel?: "last" | "whatsapp" | "telegram" | "discord" | "signal" | "imessage"; to?: string; thinking?: string; timeoutSeconds?: number; @@ -50,7 +50,7 @@ export type HookAction = wakeMode: "now" | "next-heartbeat"; sessionKey?: string; deliver?: boolean; - channel?: "last" | "whatsapp" | "telegram" | "discord"; + channel?: "last" | "whatsapp" | "telegram" | "discord" | "signal" | "imessage"; to?: string; thinking?: string; timeoutSeconds?: number;