xworkmate-bridge/internal/acp/config.go

242 lines
6.4 KiB
Go

package acp
import (
"os"
"strings"
"gopkg.in/yaml.v3"
"xworkmate-bridge/internal/shared"
)
type syncedProvider struct {
ProviderID string
Label string
Endpoint string
AuthorizationHeader string
Enabled bool
}
type BridgeConfig struct {
Upstream struct {
GatewayURL string `yaml:"gateway_url"`
CodexURL string `yaml:"codex_url"`
OpenCodeURL string `yaml:"opencode_url"`
GeminiURL string `yaml:"gemini_url"`
HermesURL string `yaml:"hermes_url"`
} `yaml:"upstream"`
Distributed DistributedConfig `yaml:"distributed"`
OpenClawGateway OpenClawGatewayConfig `yaml:"openclaw_gateway"`
}
type DistributedConfig struct {
TaskForwardToken string `yaml:"task_forward_token"`
Topology string `yaml:"topology"`
LocalNodeID string `yaml:"local_node_id"`
TaskForwardPeerID string `yaml:"task_forward_peer_id"`
Nodes []DistributedNodeConfig `yaml:"nodes"`
Forwarding DistributedForwardingConfig `yaml:"forwarding"`
}
type DistributedNodeConfig struct {
ID string `yaml:"id"`
Role string `yaml:"role"`
Zone string `yaml:"zone"`
PublicBaseURL string `yaml:"public_base_url"`
BridgeEndpoint string `yaml:"bridge_endpoint"`
Capabilities []string `yaml:"capabilities"`
}
type DistributedForwardingConfig struct {
HopLimit int `yaml:"hop_limit"`
DefaultAction string `yaml:"default_action"`
Rules []DistributedForwardRuleConfig `yaml:"rules"`
Routes []DistributedRouteConfig `yaml:"routes"`
}
type DistributedForwardRuleConfig struct {
Methods []string `yaml:"methods"`
Target DistributedForwardTargetConfig `yaml:"target"`
}
type DistributedForwardTargetConfig struct {
NodeID string `yaml:"node_id"`
Selector DistributedForwardSelectorConfig `yaml:"selector"`
Strategy string `yaml:"strategy"`
}
type DistributedForwardSelectorConfig struct {
Role string `yaml:"role"`
Zone string `yaml:"zone"`
Capability string `yaml:"capability"`
}
type DistributedRouteConfig struct {
TargetNodeID string `yaml:"target_node_id"`
NextHopNodeID string `yaml:"next_hop_node_id"`
}
type OpenClawGatewayConfig struct {
MaxActive *int `yaml:"max_active"`
MaxQueued *int `yaml:"max_queued"`
QueueTimeout string `yaml:"queue_timeout"`
MaxAllowedSilentDuration string `yaml:"max_allowed_silent_duration"`
}
func loadBridgeConfig() *BridgeConfig {
config := &BridgeConfig{}
configPath := shared.EnvOrDefault("BRIDGE_CONFIG_PATH", "/app/config.yaml")
if _, err := os.Stat(configPath); err == nil {
data, err := os.ReadFile(configPath)
if err == nil {
_ = yaml.Unmarshal(data, config)
}
}
return config
}
func resolveURL(yamlVal string, envKeys ...string) string {
val := strings.TrimSpace(yamlVal)
if val != "" {
return val
}
for _, key := range envKeys {
if v := strings.TrimSpace(os.Getenv(key)); v != "" {
return v
}
}
return ""
}
func bridgeUpstreamAuthorizationHeader() string {
token := bridgePublicAuthToken()
if token != "" && !strings.HasPrefix(strings.ToLower(token), "bearer ") {
return "Bearer " + token
}
return token
}
func bridgePublicAuthToken() string {
if token := strings.TrimSpace(os.Getenv("AI_WORKSPACE_AUTH_TOKEN")); token != "" {
return token
}
return strings.TrimSpace(shared.EnvOrDefault("BRIDGE_AUTH_TOKEN", ""))
}
func bridgeSharedAuthToken() string {
return bridgePublicAuthToken()
}
func bridgeInboundAuthTokens() []string {
var tokens []string
seen := map[string]struct{}{}
for _, token := range []string{
os.Getenv("AI_WORKSPACE_AUTH_TOKEN"),
os.Getenv("BRIDGE_AUTH_TOKEN"),
os.Getenv("BRIDGE_REVIEW_AUTH_TOKEN"),
} {
trimmed := strings.TrimSpace(token)
if trimmed == "" {
continue
}
if _, ok := seen[trimmed]; ok {
continue
}
seen[trimmed] = struct{}{}
tokens = append(tokens, trimmed)
}
return tokens
}
func resolveDistributedTaskForwardToken(config *BridgeConfig) string {
if token := strings.TrimSpace(os.Getenv("XWORKMATE_BRIDGE_TASK_FORWARD_TOKEN")); token != "" {
return token
}
if token := strings.TrimSpace(os.Getenv("BRIDGE_TASK_FORWARD_TOKEN")); token != "" {
return token
}
if config != nil {
if token := strings.TrimSpace(config.Distributed.TaskForwardToken); token != "" {
return token
}
}
return bridgeSharedAuthToken()
}
func defaultDistributedNodes() []DistributedNodeConfig {
return []DistributedNodeConfig{
{
ID: "xworkmate-bridge",
Role: "primary",
PublicBaseURL: "https://xworkmate-bridge.svc.plus",
BridgeEndpoint: "http://172.29.10.1:8787",
},
{
ID: "cn-xworkmate-bridge",
Role: "edge",
PublicBaseURL: "https://cn-xworkmate-bridge.svc.plus",
BridgeEndpoint: "http://172.29.10.2:8787",
},
}
}
func newProductionProviderCatalog() (*BridgeConfig, map[string]syncedProvider, []string) {
return newProductionProviderCatalogFromConfig(loadBridgeConfig())
}
func newProductionProviderCatalogFromConfig(config *BridgeConfig) (*BridgeConfig, map[string]syncedProvider, []string) {
if config == nil {
config = &BridgeConfig{}
}
authorizationHeader := bridgeUpstreamAuthorizationHeader()
providers := []struct {
id string
label string
yaml string
envKeys []string
}{
{
id: "codex",
label: "Codex",
yaml: config.Upstream.CodexURL,
envKeys: []string{"CODEX_RPC_URL"},
},
{
id: "opencode",
label: "OpenCode",
yaml: config.Upstream.OpenCodeURL,
envKeys: []string{"OPENCODE_RPC_URL"},
},
{
id: "gemini",
label: "Gemini",
yaml: config.Upstream.GeminiURL,
envKeys: []string{"GEMINI_RPC_URL"},
},
{
id: "hermes",
label: "Hermes",
yaml: config.Upstream.HermesURL,
envKeys: []string{"HERMES_RPC_URL"},
},
}
catalog := make(map[string]syncedProvider)
var order []string
for _, p := range providers {
endpoint := resolveURL(p.yaml, p.envKeys...)
catalog[p.id] = syncedProvider{
ProviderID: p.id,
Label: p.label,
Endpoint: endpoint,
AuthorizationHeader: authorizationHeader,
Enabled: endpoint != "",
}
order = append(order, p.id)
}
return config, catalog, order
}