Vendor OpenClaw source as Adolf fork baseline
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Adolf is a fork/vendored clone of github.com/openclaw/openclaw (v2026.6.11), free to diverge. Tree copied sans upstream .git; upstream remote added for future syncs. Node pinned to 24 (.nvmrc); engines already require >=22.19. Preserves docs/ARCHITECTURE.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LeqyaxJF2nbRXJtae2kNB2
This commit is contained in:
263
apps/macos/Sources/OpenClaw/RemoteGatewayProbe.swift
Normal file
263
apps/macos/Sources/OpenClaw/RemoteGatewayProbe.swift
Normal file
@@ -0,0 +1,263 @@
|
||||
import Foundation
|
||||
import OpenClawIPC
|
||||
import OpenClawKit
|
||||
|
||||
enum RemoteGatewayAuthIssue: Equatable {
|
||||
case tokenRequired
|
||||
case tokenMismatch
|
||||
case gatewayTokenNotConfigured
|
||||
case setupCodeExpired
|
||||
case passwordRequired
|
||||
case pairingRequired
|
||||
|
||||
init?(error: Error) {
|
||||
guard let authError = error as? GatewayConnectAuthError else {
|
||||
return nil
|
||||
}
|
||||
switch authError.detail {
|
||||
case .authTokenMissing:
|
||||
self = .tokenRequired
|
||||
case .authTokenMismatch:
|
||||
self = .tokenMismatch
|
||||
case .authTokenNotConfigured:
|
||||
self = .gatewayTokenNotConfigured
|
||||
case .authBootstrapTokenInvalid:
|
||||
self = .setupCodeExpired
|
||||
case .authPasswordMissing, .authPasswordMismatch, .authPasswordNotConfigured:
|
||||
self = .passwordRequired
|
||||
case .pairingRequired:
|
||||
self = .pairingRequired
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var showsTokenField: Bool {
|
||||
switch self {
|
||||
case .tokenRequired, .tokenMismatch:
|
||||
true
|
||||
case .gatewayTokenNotConfigured, .setupCodeExpired, .passwordRequired, .pairingRequired:
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
var title: String {
|
||||
switch self {
|
||||
case .tokenRequired:
|
||||
"This gateway requires an auth token"
|
||||
case .tokenMismatch:
|
||||
"That token did not match the gateway"
|
||||
case .gatewayTokenNotConfigured:
|
||||
"This gateway host needs token setup"
|
||||
case .setupCodeExpired:
|
||||
"This setup code is no longer valid"
|
||||
case .passwordRequired:
|
||||
"This gateway is using unsupported auth"
|
||||
case .pairingRequired:
|
||||
"This device needs pairing approval"
|
||||
}
|
||||
}
|
||||
|
||||
var body: String {
|
||||
switch self {
|
||||
case .tokenRequired:
|
||||
"Paste the token configured on the gateway host. "
|
||||
+ "On the gateway host, run `openclaw config get gateway.auth.token`. "
|
||||
+ "If the gateway uses an environment variable instead, use `OPENCLAW_GATEWAY_TOKEN`."
|
||||
case .tokenMismatch:
|
||||
"Check `gateway.auth.token` or `OPENCLAW_GATEWAY_TOKEN` on the gateway host and try again."
|
||||
case .gatewayTokenNotConfigured:
|
||||
"This gateway is set to token auth, but no `gateway.auth.token` is configured on the gateway host. "
|
||||
+ "If the gateway uses an environment variable instead, "
|
||||
+ "set `OPENCLAW_GATEWAY_TOKEN` before starting the gateway."
|
||||
case .setupCodeExpired:
|
||||
"Scan or paste a fresh setup code from an already-paired OpenClaw client, then try again."
|
||||
case .passwordRequired:
|
||||
"This onboarding flow does not support password auth yet. "
|
||||
+ "Reconfigure the gateway to use token auth, then retry."
|
||||
case .pairingRequired:
|
||||
"Approve this device from an already-paired OpenClaw client. "
|
||||
+ "In your OpenClaw chat, run `/pair approve`, then click **Check connection** again."
|
||||
}
|
||||
}
|
||||
|
||||
var footnote: String? {
|
||||
switch self {
|
||||
case .tokenRequired, .gatewayTokenNotConfigured:
|
||||
"No token yet? Generate one on the gateway host with "
|
||||
+ "`openclaw doctor --generate-gateway-token`, then set it as `gateway.auth.token`."
|
||||
case .setupCodeExpired:
|
||||
nil
|
||||
case .pairingRequired:
|
||||
"If you do not have another paired OpenClaw client yet, "
|
||||
+ "approve the pending request on the gateway host with `openclaw devices approve`."
|
||||
case .tokenMismatch, .passwordRequired:
|
||||
nil
|
||||
}
|
||||
}
|
||||
|
||||
var statusMessage: String {
|
||||
switch self {
|
||||
case .tokenRequired:
|
||||
"This gateway requires an auth token from the gateway host."
|
||||
case .tokenMismatch:
|
||||
"Gateway token mismatch. Check gateway.auth.token or OPENCLAW_GATEWAY_TOKEN on the gateway host."
|
||||
case .gatewayTokenNotConfigured:
|
||||
"This gateway has token auth enabled, but no gateway.auth.token is configured on the host."
|
||||
case .setupCodeExpired:
|
||||
"Setup code expired or already used. Scan a fresh setup code, then try again."
|
||||
case .passwordRequired:
|
||||
"This gateway uses password auth. Remote onboarding on macOS cannot collect gateway passwords yet."
|
||||
case .pairingRequired:
|
||||
"Pairing required. In an already-paired OpenClaw client, "
|
||||
+ "run /pair approve, then check the connection again."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum RemoteGatewayProbeResult: Equatable {
|
||||
case ready(RemoteGatewayProbeSuccess)
|
||||
case authIssue(RemoteGatewayAuthIssue)
|
||||
case failed(String)
|
||||
}
|
||||
|
||||
struct RemoteGatewayProbeSuccess: Equatable {
|
||||
let authSource: GatewayAuthSource?
|
||||
|
||||
var title: String {
|
||||
switch self.authSource {
|
||||
case .some(.deviceToken):
|
||||
"Connected via paired device"
|
||||
case .some(.bootstrapToken):
|
||||
"Connected with setup code"
|
||||
case .some(.sharedToken):
|
||||
"Connected with gateway token"
|
||||
case .some(.password):
|
||||
"Connected with password"
|
||||
case .some(GatewayAuthSource.none), nil:
|
||||
"Remote gateway ready"
|
||||
}
|
||||
}
|
||||
|
||||
var detail: String? {
|
||||
switch self.authSource {
|
||||
case .some(.deviceToken):
|
||||
"This Mac used a stored device token. New or unpaired devices may still need the gateway token."
|
||||
case .some(.bootstrapToken):
|
||||
"This Mac is still using the temporary setup code. "
|
||||
+ "Approve pairing to finish provisioning device-scoped auth."
|
||||
case .some(.sharedToken), .some(.password), .some(GatewayAuthSource.none), nil:
|
||||
nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum RemoteGatewayProbe {
|
||||
@MainActor
|
||||
static func run() async -> RemoteGatewayProbeResult {
|
||||
AppStateStore.shared.syncGatewayConfigNow()
|
||||
let settings = CommandResolver.connectionSettings()
|
||||
let transport = AppStateStore.shared.remoteTransport
|
||||
|
||||
if transport == .direct {
|
||||
let trimmedUrl = AppStateStore.shared.remoteUrl.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard !trimmedUrl.isEmpty else {
|
||||
return .failed("Set a gateway URL first")
|
||||
}
|
||||
guard self.isValidWsUrl(trimmedUrl) else {
|
||||
return .failed("Gateway URL must use wss:// for remote hosts (ws:// only for localhost)")
|
||||
}
|
||||
} else {
|
||||
let trimmedTarget = settings.target.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard !trimmedTarget.isEmpty else {
|
||||
return .failed("Set an SSH target first")
|
||||
}
|
||||
if let validationMessage = CommandResolver.sshTargetValidationMessage(trimmedTarget) {
|
||||
return .failed(validationMessage)
|
||||
}
|
||||
guard let sshCommand = self.sshCheckCommand(
|
||||
target: settings.target,
|
||||
identity: settings.identity,
|
||||
hostKeyPolicy: settings.sshHostKeyPolicy)
|
||||
else {
|
||||
return .failed("SSH target is invalid")
|
||||
}
|
||||
|
||||
let sshResult = await ShellExecutor.run(
|
||||
command: sshCommand,
|
||||
cwd: nil,
|
||||
env: CommandResolver.sshEnvironment(),
|
||||
timeout: 8)
|
||||
guard sshResult.ok else {
|
||||
return .failed(self.formatSSHFailure(sshResult, target: settings.target))
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
_ = try await GatewayConnection.shared.healthSnapshot(timeoutMs: 10000)
|
||||
let authSource = await GatewayConnection.shared.authSource()
|
||||
return .ready(RemoteGatewayProbeSuccess(authSource: authSource))
|
||||
} catch {
|
||||
if let authIssue = RemoteGatewayAuthIssue(error: error) {
|
||||
return .authIssue(authIssue)
|
||||
}
|
||||
return .failed(error.localizedDescription)
|
||||
}
|
||||
}
|
||||
|
||||
private static func isValidWsUrl(_ raw: String) -> Bool {
|
||||
GatewayRemoteConfig.normalizeGatewayUrl(raw) != nil
|
||||
}
|
||||
|
||||
private static func sshCheckCommand(
|
||||
target: String,
|
||||
identity: String,
|
||||
hostKeyPolicy: CommandResolver.SSHHostKeyPolicy) -> [String]?
|
||||
{
|
||||
guard let parsed = CommandResolver.parseSSHTarget(target) else { return nil }
|
||||
let options = [
|
||||
"-o", "BatchMode=yes",
|
||||
"-o", "ConnectTimeout=5",
|
||||
] + hostKeyPolicy.commandOptions
|
||||
let args = CommandResolver.sshArguments(
|
||||
target: parsed,
|
||||
identity: identity,
|
||||
options: options,
|
||||
remoteCommand: ["echo", "ok"])
|
||||
return ["/usr/bin/ssh"] + args
|
||||
}
|
||||
|
||||
#if SWIFT_PACKAGE
|
||||
static func _testSSHCheckCommand(
|
||||
target: String,
|
||||
hostKeyPolicy: CommandResolver.SSHHostKeyPolicy) -> [String]?
|
||||
{
|
||||
self.sshCheckCommand(target: target, identity: "", hostKeyPolicy: hostKeyPolicy)
|
||||
}
|
||||
#endif
|
||||
|
||||
private static func formatSSHFailure(_ response: Response, target: String) -> String {
|
||||
let payload = response.payload.flatMap { String(data: $0, encoding: .utf8) }
|
||||
let trimmed = payload?
|
||||
.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
.split(whereSeparator: \.isNewline)
|
||||
.joined(separator: " ")
|
||||
if let trimmed,
|
||||
trimmed.localizedCaseInsensitiveContains("host key verification failed")
|
||||
{
|
||||
let host = CommandResolver.parseSSHTarget(target)?.host ?? target
|
||||
return "SSH check failed: Host key verification failed. "
|
||||
+ "Remove the old key with ssh-keygen -R \(host) and try again."
|
||||
}
|
||||
if let trimmed, !trimmed.isEmpty {
|
||||
if let message = response.message, message.hasPrefix("exit ") {
|
||||
return "SSH check failed: \(trimmed) (\(message))"
|
||||
}
|
||||
return "SSH check failed: \(trimmed)"
|
||||
}
|
||||
if let message = response.message {
|
||||
return "SSH check failed (\(message))"
|
||||
}
|
||||
return "SSH check failed"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user