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 3a712ebcef..e0b3532add 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 4de00bd3e8..af1c1635cd 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 192eb3838c..b0eec702a2 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 1d29d786b7..81c0018786 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;