"""Dev only, not served by the Space: run the app locally against live HF data with every write path disabled. HF_TOKEN=... python dev/run_readonly.py [port] [--as-editor] HfApi write methods and non-GET httpx calls raise, so a local page load cannot upload, reconcile the budget ledger, launch or cancel jobs, or touch the live Space. --as-editor makes every GET and HEAD request to this local process act as a signed-in BenchFlow editor, so the dashboard can open transcripts, token views and search without an OAuth sign-in. It patches auth.principal in this process only: the Space runs app.py, which never imports this file, so its access rules are unchanged. Other methods keep the real check. """ import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parents[1])) import os import httpx import huggingface_hub from huggingface_hub import HfApi # Job logs, costs and stop reasons need a token; without one the page loads but those fields go quietly empty. if not os.environ.get('HF_TOKEN'): stored = huggingface_hub.get_token() if not stored: sys.exit('No HF token: set HF_TOKEN or run `hf auth login`; job logs, costs and stop reasons need it.') os.environ['HF_TOKEN'] = stored print('Using the locally stored HF token (read-only: every write path below is disabled).') WRITES = ('upload_file', 'upload_folder', 'upload_large_folder', 'create_commit', 'create_repo', 'delete_repo', 'delete_file', 'delete_folder', 'create_branch', 'delete_branch', 'create_tag', 'delete_tag', 'super_squash_history', 'update_repo_settings', 'move_repo', 'run_job', 'run_uv_job', 'cancel_job', 'create_scheduled_job', 'delete_scheduled_job', 'restart_space', 'pause_space', 'add_space_secret', 'delete_space_secret', 'add_space_variable', 'delete_space_variable', 'duplicate_space', 'request_space_hardware') def blocked(name): def refuse(*args, **kwargs): raise RuntimeError(f'dev read-only guard: {name} is disabled') return refuse for name in WRITES: if hasattr(HfApi, name): setattr(HfApi, name, blocked('HfApi.' + name)) if hasattr(huggingface_hub, name): setattr(huggingface_hub, name, blocked('huggingface_hub.' + name)) for name in ('post', 'put', 'patch', 'delete'): setattr(httpx, name, blocked('httpx.' + name)) _stream = httpx.stream def get_stream(method, *args, **kwargs): if method.upper() != 'GET': raise RuntimeError('dev read-only guard: httpx.stream ' + method) return _stream(method, *args, **kwargs) httpx.stream = get_stream import uvicorn import app # noqa: E402 (imported after the guard on purpose) import collab collab.system_post = blocked('collab.system_post') AS_EDITOR = '--as-editor' in sys.argv if AS_EDITOR: sys.argv.remove('--as-editor') import auth _principal = auth.principal def principal(request): if request.method in ('GET', 'HEAD'): return {'name': 'local-dev', 'orgs': [{'name': 'benchflow', 'roleInOrg': 'write'}]} return _principal(request) auth.principal = principal print('--as-editor: local GET requests act as a BenchFlow editor (this process only).') if __name__ == '__main__': uvicorn.run(app.app, host='127.0.0.1', port=int(sys.argv[1]) if len(sys.argv) > 1 else 7861, access_log=False)