feat(macos): support gateway bind config

This commit is contained in:
Peter Steinberger
2025-12-20 21:52:19 +01:00
parent e8106109e3
commit 28e5ebd72b
2 changed files with 69 additions and 6 deletions

View File

@@ -63,6 +63,7 @@ struct GatewayCommandResolution {
enum GatewayEnvironment {
private static let logger = Logger(subsystem: "com.steipete.clawdis", category: "gateway.env")
private static let supportedBindModes: Set<String> = ["loopback", "tailnet", "lan", "auto"]
static func bundledGatewayExecutable() -> String? {
guard let res = Bundle.main.resourceURL else { return nil }
@@ -198,7 +199,8 @@ enum GatewayEnvironment {
let port = self.gatewayPort()
if let bundled {
let cmd = [bundled, "gateway-daemon", "--port", "\(port)", "--bind", "loopback"]
let bind = self.preferredGatewayBind() ?? "loopback"
let cmd = [bundled, "gateway-daemon", "--port", "\(port)", "--bind", bind]
return GatewayCommandResolution(status: status, command: cmd)
}
if let gatewayBin {
@@ -216,6 +218,27 @@ enum GatewayEnvironment {
return GatewayCommandResolution(status: status, command: nil)
}
private static func preferredGatewayBind() -> String? {
if let env = ProcessInfo.processInfo.environment["CLAWDIS_GATEWAY_BIND"] {
let trimmed = env.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
if self.supportedBindModes.contains(trimmed) {
return trimmed
}
}
let root = ClawdisConfigFile.loadDict()
if let gateway = root["gateway"] as? [String: Any],
let bind = gateway["bind"] as? String
{
let trimmed = bind.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
if self.supportedBindModes.contains(trimmed) {
return trimmed
}
}
return nil
}
static func installGlobal(version: Semver?, statusHandler: @escaping @Sendable (String) -> Void) async {
let preferred = CommandResolver.preferredPaths().joined(separator: ":")
let target = version?.description ?? "latest"

View File

@@ -1,6 +1,8 @@
import Foundation
enum GatewayLaunchAgentManager {
private static let supportedBindModes: Set<String> = ["loopback", "tailnet", "lan", "auto"]
private static var plistURL: URL {
FileManager.default.homeDirectoryForCurrentUser
.appendingPathComponent("Library/LaunchAgents/\(gatewayLaunchdLabel).plist")
@@ -52,6 +54,20 @@ enum GatewayLaunchAgentManager {
let relayDir = self.relayDir(bundlePath: bundlePath)
let preferredPath = ([relayDir] + CommandResolver.preferredPaths())
.joined(separator: ":")
let bind = self.preferredGatewayBind() ?? "loopback"
let token = self.preferredGatewayToken()
var envEntries = """
<key>PATH</key>
<string>\(preferredPath)</string>
<key>CLAWDIS_IMAGE_BACKEND</key>
<string>sips</string>
"""
if let token {
envEntries += """
<key>CLAWDIS_GATEWAY_TOKEN</key>
<string>\(token)</string>
"""
}
let plist = """
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
@@ -66,7 +82,7 @@ enum GatewayLaunchAgentManager {
<string>--port</string>
<string>\(port)</string>
<string>--bind</string>
<string>loopback</string>
<string>\(bind)</string>
</array>
<key>WorkingDirectory</key>
<string>\(FileManager.default.homeDirectoryForCurrentUser.path)</string>
@@ -76,10 +92,7 @@ enum GatewayLaunchAgentManager {
<true/>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>\(preferredPath)</string>
<key>CLAWDIS_IMAGE_BACKEND</key>
<string>sips</string>
\(envEntries)
</dict>
<key>StandardOutPath</key>
<string>\(LogLocator.launchdGatewayLogPath)</string>
@@ -91,6 +104,33 @@ enum GatewayLaunchAgentManager {
try? plist.write(to: self.plistURL, atomically: true, encoding: .utf8)
}
private static func preferredGatewayBind() -> String? {
if let env = ProcessInfo.processInfo.environment["CLAWDIS_GATEWAY_BIND"] {
let trimmed = env.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
if self.supportedBindModes.contains(trimmed) {
return trimmed
}
}
let root = ClawdisConfigFile.loadDict()
if let gateway = root["gateway"] as? [String: Any],
let bind = gateway["bind"] as? String
{
let trimmed = bind.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
if self.supportedBindModes.contains(trimmed) {
return trimmed
}
}
return nil
}
private static func preferredGatewayToken() -> String? {
let raw = ProcessInfo.processInfo.environment["CLAWDIS_GATEWAY_TOKEN"] ?? ""
let trimmed = raw.trimmingCharacters(in: .whitespacesAndNewlines)
return trimmed.isEmpty ? nil : trimmed
}
private struct LaunchctlResult {
let status: Int32
let output: String