diff --git a/example/config.yaml b/example/config.yaml index c11c25c..285b59b 100644 --- a/example/config.yaml +++ b/example/config.yaml @@ -4,13 +4,13 @@ # BRIDGE_CONFIG_PATH=config.yaml ./xworkmate-bridge serve # Upstream provider endpoints -# Priority: YAML > Environment Variable (e.g. OPENCLAW_CODEX_URL) > Default Constants +# Priority: YAML > Environment Variable (e.g. CODEX_RPC_URL) > Default Constants upstream: gateway_url: "ws://127.0.0.1:18789/" - codex_url: "http://127.0.0.1:9001/acp/rpc" - opencode_url: "http://127.0.0.1:38992/acp/rpc" - gemini_url: "http://127.0.0.1:8791/acp/rpc" - hermes_url: "http://127.0.0.1:3920/acp/rpc" + codex_url: "ws://127.0.0.1:9001/acp" + opencode_url: "http://127.0.0.1:38992" + gemini_url: "http://127.0.0.1:8791" + hermes_url: "http://127.0.0.1:3920" # Legacy/Reference structure (Normally managed via code constants or environment) bridge: diff --git a/internal/acp/execution_test.go b/internal/acp/execution_test.go index b9d68e0..332b39d 100644 --- a/internal/acp/execution_test.go +++ b/internal/acp/execution_test.go @@ -16,10 +16,10 @@ func TestResolveSingleAgentForwardEndpointFromExampleConfig(t *testing.T) { } expectedEndpoints := map[string]string{ - "codex": "http://127.0.0.1:9001/acp/rpc", - "opencode": "http://127.0.0.1:38992/acp/rpc", - "gemini": "http://127.0.0.1:8791/acp/rpc", - "hermes": "http://127.0.0.1:3920/acp/rpc", + "codex": "ws://127.0.0.1:9001/acp", + "opencode": "http://127.0.0.1:38992", + "gemini": "http://127.0.0.1:8791", + "hermes": "http://127.0.0.1:3920", } for _, id := range order { diff --git a/internal/acp/provider_catalog.go b/internal/acp/provider_catalog.go index f860f71..be5f930 100644 --- a/internal/acp/provider_catalog.go +++ b/internal/acp/provider_catalog.go @@ -59,9 +59,6 @@ func resolveURL(yamlVal string, defaultVal string, envKeys ...string) string { func bridgeUpstreamAuthorizationHeader() string { token := strings.TrimSpace(shared.EnvOrDefault("BRIDGE_AUTH_TOKEN", "")) - if token == "" { - token = strings.TrimSpace(shared.EnvOrDefault("INTERNAL_SERVICE_TOKEN", "")) - } if token != "" && !strings.HasPrefix(strings.ToLower(token), "bearer ") { return "Bearer " + token } @@ -91,21 +88,21 @@ func newProductionProviderCatalog() (map[string]syncedProvider, []string) { label: "OpenCode", yaml: config.Upstream.OpenCodeURL, envKeys: []string{"OPENCODE_RPC_URL"}, - defaultURL: "http://127.0.0.1:38992/acp/rpc", + defaultURL: "http://127.0.0.1:38992", }, { id: "gemini", label: "Gemini", yaml: config.Upstream.GeminiURL, envKeys: []string{"GEMINI_RPC_URL"}, - defaultURL: "http://127.0.0.1:8791/acp/rpc", + defaultURL: "http://127.0.0.1:8791", }, { id: "hermes", label: "Hermes", yaml: config.Upstream.HermesURL, envKeys: []string{"HERMES_RPC_URL"}, - defaultURL: "http://127.0.0.1:3920/acp/rpc", + defaultURL: "http://127.0.0.1:3920", }, } diff --git a/internal/acp/providers_sync_test.go b/internal/acp/providers_sync_test.go index d947cc2..be073b0 100644 --- a/internal/acp/providers_sync_test.go +++ b/internal/acp/providers_sync_test.go @@ -108,3 +108,18 @@ func TestProductionProviderCatalogPrefersDedicatedBridgeAuthToken(t *testing.T) t.Fatalf("expected dedicated bearer header, got %q", got) } } + +func TestProductionProviderCatalogIgnoresInternalServiceToken(t *testing.T) { + t.Setenv("BRIDGE_AUTH_TOKEN", "") + t.Setenv("INTERNAL_SERVICE_TOKEN", "legacy-token") + + catalog, _ := newProductionProviderCatalog() + p, ok := catalog["codex"] + if !ok { + t.Fatal("missing codex") + } + + if got := p.AuthorizationHeader; got != "" { + t.Fatalf("expected empty auth header when BRIDGE_AUTH_TOKEN is unset, got %q", got) + } +} diff --git a/internal/acp/web_contract_test.go b/internal/acp/web_contract_test.go index 9434310..a72b1df 100644 --- a/internal/acp/web_contract_test.go +++ b/internal/acp/web_contract_test.go @@ -442,7 +442,7 @@ func TestHandleRPCCapabilitiesReturnsCanonicalProviderContract(t *testing.T) { func TestHandleRPCSessionStartSucceedsWithExplicitProvider(t *testing.T) { externalServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if got := r.Header.Get("Authorization"); got != "Bearer internal-test-token" { + if got := r.Header.Get("Authorization"); got != "Bearer bridge-test-token" { t.Fatalf("unexpected auth header: %q", got) } _ = json.NewEncoder(w).Encode(map[string]any{ @@ -457,8 +457,7 @@ func TestHandleRPCSessionStartSucceedsWithExplicitProvider(t *testing.T) { })) defer externalServer.Close() - t.Setenv("INTERNAL_SERVICE_TOKEN", "internal-test-token") - t.Setenv("BRIDGE_AUTH_TOKEN", "") + t.Setenv("BRIDGE_AUTH_TOKEN", "bridge-test-token") t.Setenv("BRIDGE_CONFIG_PATH", "../../example/config.yaml") server := NewServer() @@ -466,7 +465,7 @@ func TestHandleRPCSessionStartSucceedsWithExplicitProvider(t *testing.T) { ProviderID: "opencode", Label: "OpenCode", Endpoint: externalServer.URL, - AuthorizationHeader: "Bearer internal-test-token", + AuthorizationHeader: "Bearer bridge-test-token", Enabled: true, }) @@ -477,7 +476,7 @@ func TestHandleRPCSessionStartSucceedsWithExplicitProvider(t *testing.T) { strings.NewReader(`{"jsonrpc":"2.0","id":"task-1","method":"session.start","params":{"sessionId":"s1","threadId":"t1","taskPrompt":"Reply with exactly pong","workingDirectory":"`+t.TempDir()+`","routing":{"routingMode":"explicit","explicitExecutionTarget":"singleAgent","explicitProviderId":"opencode"}}}`), ) request.Header.Set("Content-Type", "application/json") - request.Header.Set("Authorization", "Bearer bridge-token") + request.Header.Set("Authorization", "Bearer bridge-test-token") server.HandleRPC(recorder, request) diff --git a/scripts/github-actions/test-validate-deploy.sh b/scripts/github-actions/test-validate-deploy.sh index 637fa76..8e7707c 100644 --- a/scripts/github-actions/test-validate-deploy.sh +++ b/scripts/github-actions/test-validate-deploy.sh @@ -112,6 +112,12 @@ case "${scenario}" in https://xworkmate-bridge.svc.plus/acp-server/*/acp/rpc) printf '{"jsonrpc":"2.0","result":{"providers":["ok"]}}\n' ;; + https://xworkmate-bridge.svc.plus/acp-server/*) + printf '{"jsonrpc":"2.0","result":{"providers":["ok"]}}\n' + ;; + https://xworkmate-bridge.svc.plus/gateway/openclaw) + printf '{"jsonrpc":"2.0","result":{"providers":["ok"]}}\n' + ;; https://xworkmate-bridge.svc.plus/acp/rpc) printf 'curl: (28) Operation timed out after 20001 milliseconds with 0 bytes received\n' >&2 exit 1 @@ -138,6 +144,12 @@ case "${scenario}" in https://xworkmate-bridge.svc.plus/acp-server/*/acp/rpc) printf '{"jsonrpc":"2.0","result":{"providers":["ok"]}}\n' ;; + https://xworkmate-bridge.svc.plus/acp-server/*) + printf '{"jsonrpc":"2.0","result":{"providers":["ok"]}}\n' + ;; + https://xworkmate-bridge.svc.plus/gateway/openclaw) + printf '{"jsonrpc":"2.0","result":{"providers":["ok"]}}\n' + ;; https://xworkmate-bridge.svc.plus/acp/rpc) if [[ "${data}" == *'"providerId":"codex"'* ]]; then printf '{"jsonrpc":"2.0","result":{"success":true,"providerId":"codex","capabilities":{"providers":["codex"]}}}\n' @@ -194,12 +206,12 @@ run_validate_capture() { FAKE_CURL_SCENARIO="${scenario}" \ FAKE_CURL_STATE_DIR="${RUN_STATE_DIR}" \ BRIDGE_SERVER_URL="https://xworkmate-bridge.svc.plus" \ - OPENCLAW_URL="wss://openclaw.svc.plus" \ - CODEX_RPC_URL="https://xworkmate-bridge.svc.plus/acp-server/codex/acp/rpc" \ - OPENCODE_RPC_URL="https://xworkmate-bridge.svc.plus/acp-server/opencode/acp/rpc" \ - GEMINI_RPC_URL="https://xworkmate-bridge.svc.plus/acp-server/gemini/acp/rpc" \ - HERMES_RPC_URL="https://xworkmate-bridge.svc.plus/acp-server/hermes/acp/rpc" \ - INTERNAL_SERVICE_TOKEN="test-token" \ + OPENCLAW_URL="https://xworkmate-bridge.svc.plus/gateway/openclaw" \ + CODEX_RPC_URL="https://xworkmate-bridge.svc.plus/acp-server/codex" \ + OPENCODE_RPC_URL="https://xworkmate-bridge.svc.plus/acp-server/opencode" \ + GEMINI_RPC_URL="https://xworkmate-bridge.svc.plus/acp-server/gemini" \ + HERMES_RPC_URL="https://xworkmate-bridge.svc.plus/acp-server/hermes" \ + BRIDGE_AUTH_TOKEN="test-token" \ bash "${SCRIPT_PATH}" "${IMAGE_REF}" 2>&1 )" RUN_STATUS=$? diff --git a/scripts/github-actions/validate-deploy.sh b/scripts/github-actions/validate-deploy.sh index 10883db..89f2abc 100644 --- a/scripts/github-actions/validate-deploy.sh +++ b/scripts/github-actions/validate-deploy.sh @@ -54,26 +54,22 @@ fi BASE_URL="$(normalize_url "${BRIDGE_SERVER_URL:-${2:-https://xworkmate-bridge.svc.plus}}")" OPENCLAW_BASE_URL="$(normalize_url "${OPENCLAW_URL:-${3:-${BASE_URL}/gateway/openclaw}}")" -CODEX_BASE_URL="$(normalize_url "${CODEX_RPC_URL:-${4:-${BASE_URL}/acp-server/codex/acp/rpc}}")" -OPENCODE_BASE_URL="$(normalize_url "${OPENCODE_RPC_URL:-${5:-${BASE_URL}/acp-server/opencode/acp/rpc}}")" -GEMINI_BASE_URL="$(normalize_url "${GEMINI_RPC_URL:-${6:-${BASE_URL}/acp-server/gemini/acp/rpc}}")" -HERMES_BASE_URL="$(normalize_url "${HERMES_RPC_URL:-${7:-${BASE_URL}/acp-server/hermes/acp/rpc}}")" -AUTH_TOKEN="${BRIDGE_AUTH_TOKEN:-${INTERNAL_SERVICE_TOKEN:-${7:-}}}" +CODEX_BASE_URL="$(normalize_url "${CODEX_RPC_URL:-${4:-${BASE_URL}/acp-server/codex}}")" +OPENCODE_BASE_URL="$(normalize_url "${OPENCODE_RPC_URL:-${5:-${BASE_URL}/acp-server/opencode}}")" +GEMINI_BASE_URL="$(normalize_url "${GEMINI_RPC_URL:-${6:-${BASE_URL}/acp-server/gemini}}")" +HERMES_BASE_URL="$(normalize_url "${HERMES_RPC_URL:-${7:-${BASE_URL}/acp-server/hermes}}")" +AUTH_TOKEN="${BRIDGE_AUTH_TOKEN:?BRIDGE_AUTH_TOKEN is required}" -ensure_rpc_path() { +append_rpc_path() { local url="$1" - if [[ "${url}" == */acp/rpc ]]; then - printf '%s\n' "${url}" - else - printf '%s/acp/rpc\n' "${url%/}" - fi + printf '%s/acp/rpc\n' "${url%/}" } OPENCLAW_HTTP_PROBE_URL="$(websocket_probe_url "${OPENCLAW_BASE_URL}")" -CODEX_RPC_ENDPOINT="$(ensure_rpc_path "${CODEX_BASE_URL}")" -OPENCODE_RPC_ENDPOINT="$(ensure_rpc_path "${OPENCODE_BASE_URL}")" -GEMINI_RPC_ENDPOINT="$(ensure_rpc_path "${GEMINI_BASE_URL}")" -HERMES_RPC_ENDPOINT="$(ensure_rpc_path "${HERMES_BASE_URL}")" +CODEX_RPC_ENDPOINT="$(append_rpc_path "${CODEX_BASE_URL}")" +OPENCODE_RPC_ENDPOINT="$(append_rpc_path "${OPENCODE_BASE_URL}")" +GEMINI_RPC_ENDPOINT="$(append_rpc_path "${GEMINI_BASE_URL}")" +HERMES_RPC_ENDPOINT="$(append_rpc_path "${HERMES_BASE_URL}")" fast_http_curl_common=( --silent