diff --git a/scripts/test-live-cli-backend-docker.sh b/scripts/test-live-cli-backend-docker.sh index 84b7ea5e0dd9..70f940119337 100644 --- a/scripts/test-live-cli-backend-docker.sh +++ b/scripts/test-live-cli-backend-docker.sh @@ -122,7 +122,10 @@ if [[ "$CLI_PROVIDER" == "claude-cli" && "$CLI_AUTH_MODE" == "subscription" ]]; CLAUDE_CREDS_FILE="$HOME/.claude/.credentials.json" CLAUDE_SUBSCRIPTION_AUTH_SOURCE="" CLAUDE_SUBSCRIPTION_TYPE="" - if [[ -f "$CLAUDE_CREDS_FILE" ]]; then + if [[ -n "${CLAUDE_CODE_OAUTH_TOKEN:-}" ]]; then + CLAUDE_SUBSCRIPTION_TYPE="oauth-token" + CLAUDE_SUBSCRIPTION_AUTH_SOURCE="env-token" + elif [[ -f "$CLAUDE_CREDS_FILE" ]]; then CLAUDE_SUBSCRIPTION_TYPE="$( node -e ' const fs = require("node:fs"); @@ -138,9 +141,6 @@ if [[ "$CLI_PROVIDER" == "claude-cli" && "$CLI_AUTH_MODE" == "subscription" ]]; exit 1 } CLAUDE_SUBSCRIPTION_AUTH_SOURCE="credentials-file" - elif [[ -n "${CLAUDE_CODE_OAUTH_TOKEN:-}" ]]; then - CLAUDE_SUBSCRIPTION_TYPE="oauth-token" - CLAUDE_SUBSCRIPTION_AUTH_SOURCE="env-token" else echo "ERROR: Claude subscription auth requires either:" >&2 echo " - $CLAUDE_CREDS_FILE with claudeAiOauth.subscriptionType, or" >&2 @@ -200,6 +200,16 @@ else AUTH_FILES+=("$auth_file") done < <(openclaw_live_collect_auth_files_from_csv "$CLI_PROVIDER") fi +if [[ "${CLAUDE_SUBSCRIPTION_AUTH_SOURCE:-}" == "env-token" ]]; then + retained_auth_files=() + for auth_file in "${AUTH_FILES[@]}"; do + case "$auth_file" in + .claude.json | .claude/.credentials.json) ;; + *) retained_auth_files+=("$auth_file") ;; + esac + done + AUTH_FILES=("${retained_auth_files[@]}") +fi AUTH_DIRS_CSV="" if ((${#AUTH_DIRS[@]} > 0)); then AUTH_DIRS_CSV="$(openclaw_live_join_csv "${AUTH_DIRS[@]}")" @@ -327,15 +337,15 @@ if [ "$provider" = "claude-cli" ]; then node - <<'NODE' const fs = require("node:fs"); const file = `${process.env.HOME}/.claude/.credentials.json`; -if (fs.existsSync(file)) { +if (process.env.CLAUDE_CODE_OAUTH_TOKEN?.trim()) { + console.error("[claude-subscription] using CLAUDE_CODE_OAUTH_TOKEN from environment"); +} else if (fs.existsSync(file)) { const data = JSON.parse(fs.readFileSync(file, "utf8")); const subscriptionType = String(data?.claudeAiOauth?.subscriptionType ?? "").trim(); if (!subscriptionType || subscriptionType === "unknown") { throw new Error("Claude subscription OAuth credentials are missing subscriptionType."); } console.error(`[claude-subscription] subscriptionType=${subscriptionType}`); -} else if (process.env.CLAUDE_CODE_OAUTH_TOKEN?.trim()) { - console.error("[claude-subscription] using CLAUDE_CODE_OAUTH_TOKEN from environment"); } else { throw new Error("Claude subscription OAuth token or credentials file is required."); } @@ -365,17 +375,30 @@ WRAP if [ "$auth_mode" = "subscription" ]; then claude --version direct_token="OPENCLAW-CLAUDE-SUBSCRIPTION-DIRECT" - direct_output="$( - claude \ - -p "Reply exactly: $direct_token" \ - --output-format text \ - --model sonnet \ - --permission-mode bypassPermissions \ - --setting-sources user \ - --strict-mcp-config \ - --mcp-config '{"mcpServers":{}}' \ - --no-session-persistence - )" + direct_probe_log="$(mktemp)" + set +e + claude \ + -p "Reply exactly: $direct_token" \ + --output-format text \ + --model sonnet \ + --permission-mode bypassPermissions \ + --setting-sources user \ + --strict-mcp-config \ + --mcp-config '{"mcpServers":{}}' \ + --no-session-persistence >"$direct_probe_log" 2>&1 + direct_probe_status=$? + set -e + direct_output="$(<"$direct_probe_log")" + if [ "$direct_probe_status" -ne 0 ]; then + echo "ERROR: direct Claude subscription probe exited with status $direct_probe_status." >&2 + sed -E \ + -e 's/[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}//g' \ + -e 's/(sk-ant-|sk-)[A-Za-z0-9_-]+//g' \ + "$direct_probe_log" >&2 + rm -f "$direct_probe_log" + exit "$direct_probe_status" + fi + rm -f "$direct_probe_log" if [[ "$direct_output" != *"$direct_token"* ]]; then echo "ERROR: direct Claude subscription probe did not return expected token." >&2 echo "$direct_output" >&2 diff --git a/test/scripts/test-live-cli-backend-docker.test.ts b/test/scripts/test-live-cli-backend-docker.test.ts index 5bdf012f2b88..dd0c4fc9f5b7 100644 --- a/test/scripts/test-live-cli-backend-docker.test.ts +++ b/test/scripts/test-live-cli-backend-docker.test.ts @@ -30,4 +30,22 @@ describe("scripts/test-live-cli-backend-docker.sh", () => { expect(forwardedVars).toContain("OPENCLAW_LIVE_CLI_BACKEND_RESUME_ARGS"); expect(forwardedVars).toContain("OPENCLAW_TEST_CONSOLE"); }); + + it("prints redacted Claude subscription probe failures", () => { + const script = fs.readFileSync(SCRIPT_PATH, "utf8"); + + expect(script).toContain('direct_probe_log="$(mktemp)"'); + expect(script).toContain("direct Claude subscription probe exited with status"); + expect(script).toContain(""); + expect(script).toContain(""); + }); + + it("prefers explicit Claude setup tokens over staged credentials", () => { + const script = fs.readFileSync(SCRIPT_PATH, "utf8"); + + expect(script).toMatch( + /if \[\[ -n "\$\{CLAUDE_CODE_OAUTH_TOKEN:-\}" \]\]; then[\s\S]*?CLAUDE_SUBSCRIPTION_AUTH_SOURCE="env-token"[\s\S]*?elif \[\[ -f "\$CLAUDE_CREDS_FILE" \]\]; then/, + ); + expect(script).toContain(".claude.json | .claude/.credentials.json) ;;"); + }); });