#!/usr/bin/env python3 """ Watches OpenClaw device-auth.json and restores operator scopes when stripped. Runs as a persistent systemd service alongside openclaw-gateway. """ import json, glob, os, time, subprocess, sys BASE = os.path.expanduser('~/.openclaw') DEVICE_AUTH = f'{BASE}/identity/device-auth.json' SCOPES = ['operator.write', 'operator.read'] CHECK_INTERVAL = 30 # seconds def get_scopes(): try: with open(DEVICE_AUTH) as f: return json.load(f).get('scopes') or [] except: return None def restore_scopes(): fixed = [] # Fix device-auth.json try: with open(DEVICE_AUTH) as f: d = json.load(f) if d.get('scopes') != SCOPES: d['scopes'] = SCOPES with open(DEVICE_AUTH, 'w') as f: json.dump(d, f, indent=2) fixed.append('device-auth.json') except Exception as e: print(f'[scope-watchdog] device-auth error: {e}', file=sys.stderr) # Fix devices/*.json for p in glob.glob(f'{BASE}/devices/*.json'): try: with open(p) as f: data = json.load(f) changed = False items = data if isinstance(data, list) else [data] for item in items: if isinstance(item, dict) and item.get('scopes') != SCOPES: item['scopes'] = SCOPES changed = True if changed: with open(p, 'w') as f: json.dump(data, f, indent=2) fixed.append(os.path.basename(p)) except: pass return fixed print('[scope-watchdog] Starting. Checking every 30s.', flush=True) # Initial delay to let gateway fully start time.sleep(15) while True: scopes = get_scopes() if scopes is None: print('[scope-watchdog] device-auth.json not found, waiting...', flush=True) elif scopes != SCOPES: print(f'[scope-watchdog] Scopes stripped ({scopes}), restoring...', flush=True) fixed = restore_scopes() if fixed: print(f'[scope-watchdog] Restored scopes in: {fixed}', flush=True) time.sleep(CHECK_INTERVAL)