#!/bin/bash # fix-auth-profiles.sh — Fix broken Anthropic auth profiles in all OpenClaw agents # # Problem: Auth profiles may have {type:"oauth", key:"sk-ant-oat01-..."} # but OpenClaw's isValidProfile() for type:"oauth" checks for "access" field, not "key" # This causes the profile to be skipped and fall through to env var fallback # # Fix: Change "key" -> "access" field, clear cooldown stats # # Usage: # ./fix-auth-profiles.sh # auto-detect config dir # ./fix-auth-profiles.sh /path/to/.openclaw # custom config dir set -euo pipefail OPENCLAW_CONFIG_DIR="${1:-}" LOG_PREFIX="[fix-auth-profiles]" log() { echo "$LOG_PREFIX $*"; } error() { echo "$LOG_PREFIX ERROR: $*" >&2; } # Auto-detect config dir if not provided if [ -z "$OPENCLAW_CONFIG_DIR" ]; then if [ -d "/root/.openclaw/agents" ]; then OPENCLAW_CONFIG_DIR="/root/.openclaw" elif [ -d "$HOME/.openclaw/agents" ]; then OPENCLAW_CONFIG_DIR="$HOME/.openclaw" else error "Cannot find OpenClaw config directory. Provide path as argument." exit 1 fi fi log "Config directory: $OPENCLAW_CONFIG_DIR" AGENTS_DIR="$OPENCLAW_CONFIG_DIR/agents" if [ ! -d "$AGENTS_DIR" ]; then error "Agents directory not found: $AGENTS_DIR" exit 1 fi # Get current token from .env if available TOKEN="" for env_file in /root/openclaw/.env "$OPENCLAW_CONFIG_DIR/../openclaw/.env"; do if [ -f "$env_file" ]; then TOKEN=$(grep -oP 'ANTHROPIC_OAUTH_TOKEN="\K[^"]+' "$env_file" 2>/dev/null || true) [ -n "$TOKEN" ] && break fi done FIXED=0 SKIPPED=0 for agent_dir in "$AGENTS_DIR"/*/agent; do agent=$(basename "$(dirname "$agent_dir")") f="$agent_dir/auth-profiles.json" if [ ! -f "$f" ]; then log "SKIP $agent: no auth-profiles.json" SKIPPED=$((SKIPPED + 1)) continue fi log "Checking $agent..." python3 -c " import json, sys with open('$f') as fh: data = json.load(fh) changed = False # Fix profile structure if 'anthropic:default' in data.get('profiles', {}): p = data['profiles']['anthropic:default'] # Ensure type is oauth if p.get('type') != 'oauth': p['type'] = 'oauth' changed = True # Ensure provider is anthropic if p.get('provider') != 'anthropic': p['provider'] = 'anthropic' changed = True # Move key -> access if needed if 'key' in p and 'access' not in p: p['access'] = p.pop('key') changed = True print(' Fixed: key -> access') elif 'key' in p and 'access' in p: del p['key'] changed = True print(' Fixed: removed duplicate key field') # Update token if provided token = '$TOKEN' if token and p.get('access') != token: p['access'] = token changed = True print(f' Updated token: {token[:20]}...') if not changed: print(' Already correct') else: # Create the profile if it doesn't exist token = '$TOKEN' if token: if 'profiles' not in data: data['profiles'] = {} data['profiles']['anthropic:default'] = { 'type': 'oauth', 'provider': 'anthropic', 'access': token } changed = True print(' Created anthropic:default profile') else: print(' No anthropic:default profile and no token to create one') sys.exit(0) # Clear cooldown for anthropic profile if 'usageStats' in data and 'anthropic:default' in data['usageStats']: stats = data['usageStats']['anthropic:default'] for key in ['cooldownUntil', 'errorCount', 'failureCounts', 'lastFailureAt']: if key in stats: del stats[key] changed = True if changed: print(' Cleared cooldown stats') # Ensure lastGood points to anthropic:default if data.get('lastGood', {}).get('anthropic') != 'anthropic:default': if 'lastGood' not in data: data['lastGood'] = {} data['lastGood']['anthropic'] = 'anthropic:default' changed = True if changed: with open('$f', 'w') as fh: json.dump(data, fh, indent=2) print(' Saved') " FIXED=$((FIXED + 1)) done log "" log "Done. Fixed: $FIXED agents, Skipped: $SKIPPED" log "" log "Restart gateway to apply changes:" log " cd /root/openclaw && docker compose down openclaw-gateway && docker compose up -d openclaw-gateway"