#!/usr/bin/env bash set -euo pipefail EXPECTED_COMMIT="${1:?expected bridge binary commit is required}" RETRYABLE_TRANSPORT=10 RETRYABLE_NOT_READY=11 FAST_HTTP_TIMEOUT_SECONDS=20 BRIDGE_RPC_TIMEOUT_SECONDS=60 normalize_url() { local value="$1" if [[ "${value}" =~ ^https:([^/].*)$ ]]; then printf 'https://%s\n' "${BASH_REMATCH[1]}" return fi if [[ "${value}" =~ ^http:([^/].*)$ ]]; then printf 'http://%s\n' "${BASH_REMATCH[1]}" return fi printf '%s\n' "${value}" } expected_commit="$(printf '%s' "${EXPECTED_COMMIT}" | tr -d '\n' | xargs)" if [[ ! "${expected_commit}" =~ ^[0-9a-f]{7,40}$ ]]; then echo "invalid expected bridge binary commit: ${expected_commit}" >&2 exit 2 fi BASE_URL="$(normalize_url "${BRIDGE_SERVER_URL:-${2:-https://xworkmate-bridge.svc.plus}}")" RPC_URL="${BASE_URL%/}/acp/rpc" AUTH_TOKEN="${AI_WORKSPACE_AUTH_TOKEN:-${BRIDGE_AUTH_TOKEN:-}}" if [[ -z "${AUTH_TOKEN}" ]]; then echo "AI_WORKSPACE_AUTH_TOKEN or BRIDGE_AUTH_TOKEN is required" >&2 exit 1 fi fast_http_curl_common=( --silent --show-error --fail --location --max-time "${FAST_HTTP_TIMEOUT_SECONDS}" -H "Authorization: Bearer ${AUTH_TOKEN}" ) bridge_rpc_curl_common=( --silent --show-error --fail --location --max-time "${BRIDGE_RPC_TIMEOUT_SECONDS}" ) capture_http_response() { local label="$1" shift local response if ! response="$(curl "$@" 2>&1)"; then printf '%s request failed: %s\n' "${label}" "${response}" >&2 return "${RETRYABLE_TRANSPORT}" fi if [[ -z "${response}" ]]; then printf '%s request returned an empty response\n' "${label}" >&2 return "${RETRYABLE_TRANSPORT}" fi printf '%s\n' "${response}" } should_retry_exit_code() { local exit_code="$1" local allowed="$2" local candidate IFS=',' read -r -a candidates <<<"${allowed}" for candidate in "${candidates[@]}"; do if [[ "${exit_code}" == "${candidate}" ]]; then return 0 fi done return 1 } run_with_retry() { local label="$1" local attempts="$2" local sleep_seconds="$3" local retryable_codes="$4" shift 4 local attempt exit_code for ((attempt = 1; attempt <= attempts; attempt += 1)); do if "$@"; then return 0 else exit_code=$? fi if (( attempt == attempts )) || ! should_retry_exit_code "${exit_code}" "${retryable_codes}"; then return "${exit_code}" fi printf '%s attempt %d/%d failed; retrying in %ss\n' \ "${label}" \ "${attempt}" \ "${attempts}" \ "${sleep_seconds}" >&2 sleep "${sleep_seconds}" done return 1 } jsonrpc_bridge_call() { local payload="$1" capture_http_response \ "bridge rpc ${RPC_URL}" \ "${bridge_rpc_curl_common[@]}" \ -H 'Content-Type: application/json' \ -H 'Accept: application/json' \ -H "Authorization: Bearer ${AUTH_TOKEN}" \ --data "${payload}" \ "${RPC_URL}" } wait_for_release_ping_once() { local ping_json if ping_json="$( capture_http_response "bridge ping ${BASE_URL}/api/ping" \ "${fast_http_curl_common[@]}" \ "${BASE_URL%/}/api/ping" )"; then : else local exit_code=$? return "${exit_code}" fi if PING_JSON="${ping_json}" python3 - "${expected_commit}" <<'PY' import json import os import sys expected_commit = sys.argv[1] payload = json.loads(os.environ["PING_JSON"]) if payload.get("status") != "ok": raise SystemExit("ping status not ok") if payload.get("image") or payload.get("tag"): raise SystemExit("stale image/tag runtime metadata is still exposed") if payload.get("commit") != expected_commit: raise SystemExit(f"expected binary commit {expected_commit!r}, got {payload.get('commit')!r}") if not str(payload.get("version") or "").strip(): raise SystemExit("bridge binary version is empty") PY then return 0 fi return "${RETRYABLE_NOT_READY}" } probe_bridge_root() { local bridge_root if bridge_root="$( capture_http_response "bridge root ${BASE_URL}/" \ "${fast_http_curl_common[@]}" \ "${BASE_URL%/}/" )"; then : else local exit_code=$? return "${exit_code}" fi grep -qi 'xworkmate-bridge' <<<"${bridge_root}" } probe_bridge_capabilities_once() { local response if response="$(jsonrpc_bridge_call '{"jsonrpc":"2.0","id":"cap-1","method":"acp.capabilities","params":{}}')"; then : else local exit_code=$? return "${exit_code}" fi RESPONSE_JSON="${response}" python3 - <<'PY' import json import os payload = json.loads(os.environ["RESPONSE_JSON"]) result = payload.get("result") if payload.get("jsonrpc") != "2.0" or not isinstance(result, dict): raise SystemExit("capabilities response missing result payload") provider_catalog = result.get("providerCatalog") gateway_providers = result.get("gatewayProviders") targets = result.get("availableExecutionTargets") if not isinstance(provider_catalog, list): raise SystemExit("providerCatalog missing") if not isinstance(gateway_providers, list): raise SystemExit("gatewayProviders missing") if not isinstance(targets, list): raise SystemExit("availableExecutionTargets missing") providers = {item.get("providerId") for item in provider_catalog if isinstance(item, dict)} if not {"codex", "opencode", "gemini", "hermes"}.issubset(providers): raise SystemExit(f"unexpected providerCatalog: {provider_catalog!r}") gateway_ids = {item.get("providerId") for item in gateway_providers if isinstance(item, dict)} if "openclaw" not in gateway_ids: raise SystemExit(f"unexpected gatewayProviders: {gateway_providers!r}") if "agent" not in targets or "gateway" not in targets: raise SystemExit(f"unexpected availableExecutionTargets: {targets!r}") PY } probe_bridge_routing_once() { local response local payload='{"jsonrpc":"2.0","id":"route-1","method":"xworkmate.routing.resolve","params":{"taskPrompt":"create a powerpoint deck for launch","workingDirectory":"/tmp/validate-deploy","routing":{"routingMode":"explicit","explicitExecutionTarget":"singleAgent","explicitProviderId":"codex","availableSkills":[{"id":"pptx","label":"PPTX","description":"slides","installed":true}]}}}' if response="$(jsonrpc_bridge_call "${payload}")"; then : else local exit_code=$? return "${exit_code}" fi RESPONSE_JSON="${response}" python3 - <<'PY' import json import os payload = json.loads(os.environ["RESPONSE_JSON"]) result = payload.get("result") if payload.get("jsonrpc") != "2.0" or not isinstance(result, dict): raise SystemExit("routing response missing result payload") if result.get("resolvedExecutionTarget") != "single-agent": raise SystemExit(f"unexpected routing target: {result!r}") if result.get("resolvedProviderId") != "codex": raise SystemExit(f"unexpected routing provider: {result!r}") if result.get("status") != "available": raise SystemExit(f"unexpected routing status: {result!r}") PY } probe_bridge_gateway_routing_once() { local response local payload='{"jsonrpc":"2.0","id":"route-gateway-1","method":"xworkmate.routing.resolve","params":{"taskPrompt":"search latest news","workingDirectory":"/tmp/validate-deploy","routing":{"routingMode":"explicit","explicitExecutionTarget":"gateway","preferredGatewayProviderId":"openclaw"}}}' if response="$(jsonrpc_bridge_call "${payload}")"; then : else local exit_code=$? return "${exit_code}" fi RESPONSE_JSON="${response}" python3 - <<'PY' import json import os payload = json.loads(os.environ["RESPONSE_JSON"]) result = payload.get("result") if payload.get("jsonrpc") != "2.0" or not isinstance(result, dict): raise SystemExit("gateway routing response missing result payload") if result.get("resolvedExecutionTarget") != "gateway": raise SystemExit(f"unexpected gateway target: {result!r}") if result.get("resolvedGatewayProviderId") != "openclaw": raise SystemExit(f"unexpected gateway provider: {result!r}") if result.get("status") != "available": raise SystemExit(f"unexpected gateway routing status: {result!r}") PY } run_with_retry "bridge ping ${BASE_URL}/api/ping" 6 5 "${RETRYABLE_TRANSPORT},${RETRYABLE_NOT_READY}" wait_for_release_ping_once probe_bridge_root run_with_retry "bridge capabilities ${RPC_URL}" 3 5 "${RETRYABLE_TRANSPORT}" probe_bridge_capabilities_once run_with_retry "bridge routing ${RPC_URL}" 3 5 "${RETRYABLE_TRANSPORT}" probe_bridge_routing_once run_with_retry "bridge gateway routing ${RPC_URL}" 3 5 "${RETRYABLE_TRANSPORT}" probe_bridge_gateway_routing_once