xworkmate-bridge/internal/acp/gateway_runtime.go
2026-04-11 13:50:11 +08:00

217 lines
6.6 KiB
Go

package acp
import (
"net/url"
"strings"
"time"
"xworkmate-bridge/internal/gatewayruntime"
"xworkmate-bridge/internal/shared"
)
func handleGatewayConnect(
server *Server,
params map[string]any,
notify func(map[string]any),
) map[string]any {
request := gatewayruntime.ConnectRequest{
RuntimeID: strings.TrimSpace(shared.StringArg(params, "runtimeId", "")),
Mode: strings.TrimSpace(shared.StringArg(params, "mode", "unconfigured")),
ClientID: strings.TrimSpace(shared.StringArg(params, "clientId", "")),
Locale: strings.TrimSpace(shared.StringArg(params, "locale", "")),
UserAgent: strings.TrimSpace(shared.StringArg(params, "userAgent", "")),
ConnectAuthMode: strings.TrimSpace(shared.StringArg(params, "connectAuthMode", "")),
ConnectAuthFields: parseGatewayRuntimeStringSlice(params["connectAuthFields"]),
ConnectAuthSources: parseGatewayRuntimeStringSlice(params["connectAuthSources"]),
HasSharedAuth: parseBool(params["hasSharedAuth"]),
HasDeviceToken: parseBool(params["hasDeviceToken"]),
Endpoint: gatewayruntime.Endpoint{
Host: strings.TrimSpace(shared.StringArg(asMap(params["endpoint"]), "host", "")),
Port: parsePositiveInt(asMap(params["endpoint"])["port"]),
TLS: parseBool(asMap(params["endpoint"])["tls"]),
},
PackageInfo: gatewayruntime.PackageInfo{
AppName: strings.TrimSpace(shared.StringArg(asMap(params["packageInfo"]), "appName", "")),
PackageName: strings.TrimSpace(shared.StringArg(asMap(params["packageInfo"]), "packageName", "")),
Version: strings.TrimSpace(shared.StringArg(asMap(params["packageInfo"]), "version", "")),
BuildNumber: strings.TrimSpace(shared.StringArg(asMap(params["packageInfo"]), "buildNumber", "")),
},
DeviceInfo: gatewayruntime.DeviceInfo{
Platform: strings.TrimSpace(shared.StringArg(asMap(params["deviceInfo"]), "platform", "")),
PlatformVersion: strings.TrimSpace(shared.StringArg(asMap(params["deviceInfo"]), "platformVersion", "")),
DeviceFamily: strings.TrimSpace(shared.StringArg(asMap(params["deviceInfo"]), "deviceFamily", "")),
ModelIdentifier: strings.TrimSpace(shared.StringArg(asMap(params["deviceInfo"]), "modelIdentifier", "")),
},
Identity: gatewayruntime.DeviceIdentity{
DeviceID: strings.TrimSpace(shared.StringArg(asMap(params["identity"]), "deviceId", "")),
PublicKeyBase64URL: strings.TrimSpace(shared.StringArg(asMap(params["identity"]), "publicKeyBase64Url", "")),
PrivateKeyBase64URL: strings.TrimSpace(shared.StringArg(asMap(params["identity"]), "privateKeyBase64Url", "")),
},
Auth: gatewayruntime.AuthConfig{
Token: strings.TrimSpace(shared.StringArg(asMap(params["auth"]), "token", "")),
DeviceToken: strings.TrimSpace(shared.StringArg(asMap(params["auth"]), "deviceToken", "")),
Password: strings.TrimSpace(shared.StringArg(asMap(params["auth"]), "password", "")),
},
}
request = applyProductionGatewayRouting(request)
request.ReportedRemoteAddress = resolveGatewayReportedRemoteAddress(server, request)
result := server.gateway.Connect(request, notify)
return map[string]any{
"ok": result.OK,
"snapshot": result.Snapshot,
"auth": result.Auth,
"returnedDeviceToken": result.ReturnedDeviceToken,
"error": result.Error,
}
}
func applyProductionGatewayRouting(
request gatewayruntime.ConnectRequest,
) gatewayruntime.ConnectRequest {
if strings.TrimSpace(strings.ToLower(request.Mode)) != "remote" {
return request
}
request.Endpoint = gatewayruntime.Endpoint{
Host: "openclaw.svc.plus",
Port: 443,
TLS: true,
}
request.Auth.Token = strings.TrimSpace(
shared.EnvOrDefault("INTERNAL_SERVICE_TOKEN", ""),
)
request.Auth.Password = ""
request.ConnectAuthMode = "shared-token"
request.ConnectAuthFields = []string{"token"}
request.ConnectAuthSources = []string{"bridge"}
request.HasSharedAuth = request.Auth.Token != ""
return request
}
func handleGatewayRequest(
server *Server,
params map[string]any,
notify func(map[string]any),
) map[string]any {
timeout := time.Duration(parsePositiveInt(params["timeoutMs"])) * time.Millisecond
result := server.gateway.Request(
strings.TrimSpace(shared.StringArg(params, "runtimeId", "")),
strings.TrimSpace(shared.StringArg(params, "method", "")),
asMap(params["params"]),
timeout,
notify,
)
return map[string]any{
"ok": result.OK,
"payload": result.Payload,
"error": result.Error,
}
}
func handleGatewayDisconnect(
server *Server,
params map[string]any,
notify func(map[string]any),
) map[string]any {
server.gateway.Disconnect(
strings.TrimSpace(shared.StringArg(params, "runtimeId", "")),
notify,
)
return map[string]any{"accepted": true}
}
func asMap(value any) map[string]any {
if typed, ok := value.(map[string]any); ok {
return typed
}
if typed, ok := value.(map[string]interface{}); ok {
return typed
}
return map[string]any{}
}
func parseGatewayRuntimeStringSlice(value any) []string {
list, ok := value.([]any)
if !ok {
if typed, ok := value.([]string); ok {
return append([]string(nil), typed...)
}
return nil
}
result := make([]string, 0, len(list))
for _, item := range list {
text := strings.TrimSpace(shared.StringArg(map[string]any{"value": item}, "value", ""))
if text == "" {
continue
}
result = append(result, text)
}
return result
}
func parseBool(value any) bool {
switch typed := value.(type) {
case bool:
return typed
case string:
return shared.BoolArg(typed, false)
case float64:
return typed != 0
case int:
return typed != 0
default:
return false
}
}
func parsePositiveInt(value any) int {
switch typed := value.(type) {
case int:
if typed > 0 {
return typed
}
case int64:
if typed > 0 {
return int(typed)
}
case float64:
if typed > 0 {
return int(typed)
}
case string:
return shared.IntArg(typed, 0)
}
return 0
}
func resolveGatewayReportedRemoteAddress(
server *Server,
request gatewayruntime.ConnectRequest,
) string {
if strings.TrimSpace(strings.ToLower(request.Mode)) != "remote" {
return ""
}
_ = server
return publicEndpointAddressLabel(productionGatewayEndpointURL)
}
func publicEndpointAddressLabel(raw string) string {
parsed, err := url.Parse(strings.TrimSpace(raw))
if err != nil || strings.TrimSpace(parsed.Hostname()) == "" {
return ""
}
host := strings.TrimSpace(parsed.Hostname())
port := strings.TrimSpace(parsed.Port())
if port == "" {
switch strings.TrimSpace(strings.ToLower(parsed.Scheme)) {
case "https", "wss":
port = "443"
case "http", "ws":
port = "80"
}
}
if port == "" {
return host
}
return host + ":" + port
}