Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 46 additions & 12 deletions samples/CameraAccess/CameraAccess/OpenClaw/OpenClawBridge.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,25 +38,58 @@ class OpenClawBridge: ObservableObject {
return
}
connectionState = .checking
guard let url = URL(string: "\(GeminiConfig.openClawHost):\(GeminiConfig.openClawPort)/v1/chat/completions") else {

// Step 1: Check gateway health endpoint
guard let healthURL = URL(string: "\(GeminiConfig.openClawHost):\(GeminiConfig.openClawPort)/health") else {
connectionState = .unreachable("Invalid URL")
return
}
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("Bearer \(GeminiConfig.openClawGatewayToken)", forHTTPHeaderField: "Authorization")
request.setValue("glass", forHTTPHeaderField: "x-openclaw-message-channel")
var healthRequest = URLRequest(url: healthURL)
healthRequest.httpMethod = "GET"
do {
let (_, response) = try await pingSession.data(for: request)
if let http = response as? HTTPURLResponse, (200...499).contains(http.statusCode) {
connectionState = .connected
NSLog("[OpenClaw] Gateway reachable (HTTP %d)", http.statusCode)
} else {
connectionState = .unreachable("Unexpected response")
let (_, healthResponse) = try await pingSession.data(for: healthRequest)
if let http = healthResponse as? HTTPURLResponse, !(200...299).contains(http.statusCode) {
connectionState = .unreachable("Gateway not running (HTTP \(http.statusCode))")
NSLog("[OpenClaw] Gateway health check failed: HTTP %d", http.statusCode)
return
}
} catch {
connectionState = .unreachable(error.localizedDescription)
connectionState = .unreachable("Gateway unreachable: \(error.localizedDescription)")
NSLog("[OpenClaw] Gateway unreachable: %@", error.localizedDescription)
return
}

// Step 2: Verify chat completions endpoint is enabled
guard let chatURL = URL(string: "\(GeminiConfig.openClawHost):\(GeminiConfig.openClawPort)/v1/chat/completions") else {
connectionState = .unreachable("Invalid URL")
return
}
var chatRequest = URLRequest(url: chatURL)
chatRequest.httpMethod = "GET"
chatRequest.setValue("Bearer \(GeminiConfig.openClawGatewayToken)", forHTTPHeaderField: "Authorization")
chatRequest.setValue("glass", forHTTPHeaderField: "x-openclaw-message-channel")
do {
let (_, chatResponse) = try await pingSession.data(for: chatRequest)
if let http = chatResponse as? HTTPURLResponse {
switch http.statusCode {
case 200...299, 405:
// 405 Method Not Allowed on GET is expected — endpoint exists and is enabled
connectionState = .connected
NSLog("[OpenClaw] Gateway connected (HTTP %d)", http.statusCode)
case 401, 403:
connectionState = .unreachable("Authentication failed (HTTP \(http.statusCode)) — check your gateway token")
NSLog("[OpenClaw] Auth failed: HTTP %d", http.statusCode)
case 404:
connectionState = .unreachable("chatCompletions endpoint disabled — enable it in openclaw.json")
NSLog("[OpenClaw] Endpoint disabled: HTTP 404. Set gateway.http.endpoints.chatCompletions.enabled = true in ~/.openclaw/openclaw.json")
default:
connectionState = .unreachable("Unexpected response (HTTP \(http.statusCode))")
NSLog("[OpenClaw] Unexpected status: HTTP %d", http.statusCode)
}
}
} catch {
connectionState = .unreachable(error.localizedDescription)
NSLog("[OpenClaw] Chat endpoint check failed: %@", error.localizedDescription)
}
}

Expand Down Expand Up @@ -92,6 +125,7 @@ class OpenClawBridge: ObservableObject {
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue(sessionKey, forHTTPHeaderField: "x-openclaw-session-key")
request.setValue("glass", forHTTPHeaderField: "x-openclaw-message-channel")
request.setValue("operator.write", forHTTPHeaderField: "x-openclaw-scopes")

let body: [String: Any] = [
"model": "openclaw",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,28 +51,69 @@ class OpenClawBridge {
}
_connectionState.value = OpenClawConnectionState.Checking

val url = "${GeminiConfig.openClawHost}:${GeminiConfig.openClawPort}/v1/chat/completions"
// Step 1: Check gateway health endpoint
val healthUrl = "${GeminiConfig.openClawHost}:${GeminiConfig.openClawPort}/health"
try {
val request = Request.Builder()
.url(url)
val healthRequest = Request.Builder()
.url(healthUrl)
.get()
.build()

val healthResponse = pingClient.newCall(healthRequest).execute()
val healthCode = healthResponse.code
healthResponse.close()

if (healthCode !in 200..299) {
_connectionState.value = OpenClawConnectionState.Unreachable("Gateway not running (HTTP $healthCode)")
Log.d(TAG, "Gateway health check failed: HTTP $healthCode")
return@withContext
}
} catch (e: Exception) {
_connectionState.value = OpenClawConnectionState.Unreachable("Gateway unreachable: ${e.message}")
Log.d(TAG, "Gateway unreachable: ${e.message}")
return@withContext
}

// Step 2: Verify chat completions endpoint is enabled
val chatUrl = "${GeminiConfig.openClawHost}:${GeminiConfig.openClawPort}/v1/chat/completions"
try {
val chatRequest = Request.Builder()
.url(chatUrl)
.get()
.addHeader("Authorization", "Bearer ${GeminiConfig.openClawGatewayToken}")
.addHeader("x-openclaw-message-channel", "glass")
.build()

val response = pingClient.newCall(request).execute()
val code = response.code
response.close()

if (code in 200..499) {
_connectionState.value = OpenClawConnectionState.Connected
Log.d(TAG, "Gateway reachable (HTTP $code)")
} else {
_connectionState.value = OpenClawConnectionState.Unreachable("Unexpected response")
val chatResponse = pingClient.newCall(chatRequest).execute()
val code = chatResponse.code
chatResponse.close()

when (code) {
in 200..299, 405 -> {
// 405 Method Not Allowed on GET is expected — endpoint exists and is enabled
_connectionState.value = OpenClawConnectionState.Connected
Log.d(TAG, "Gateway connected (HTTP $code)")
}
401, 403 -> {
_connectionState.value = OpenClawConnectionState.Unreachable(
"Authentication failed (HTTP $code) — check your gateway token"
)
Log.d(TAG, "Auth failed: HTTP $code")
}
404 -> {
_connectionState.value = OpenClawConnectionState.Unreachable(
"chatCompletions endpoint disabled — enable it in openclaw.json"
)
Log.d(TAG, "Endpoint disabled: HTTP 404. Set gateway.http.endpoints.chatCompletions.enabled = true in ~/.openclaw/openclaw.json")
}
else -> {
_connectionState.value = OpenClawConnectionState.Unreachable("Unexpected response (HTTP $code)")
Log.d(TAG, "Unexpected status: HTTP $code")
}
}
} catch (e: Exception) {
_connectionState.value = OpenClawConnectionState.Unreachable(e.message ?: "Unknown error")
Log.d(TAG, "Gateway unreachable: ${e.message}")
Log.d(TAG, "Chat endpoint check failed: ${e.message}")
}
}

Expand Down Expand Up @@ -123,6 +164,7 @@ class OpenClawBridge {
.addHeader("Content-Type", "application/json")
.addHeader("x-openclaw-session-key", sessionKey)
.addHeader("x-openclaw-message-channel", "glass")
.addHeader("x-openclaw-scopes", "operator.write")
.build()

val response = client.newCall(request).execute()
Expand Down