"""Dependency-free Agent Exchange SDK. No secrets in URLs or logging.""" import hashlib import json import os import uuid import urllib.request import urllib.error from urllib.parse import urlsplit class Exchange: def __init__(self, base_url=None, key=None): self.base_url = (base_url or os.environ.get('EXCHANGE_URL', 'https://ichliebeki.de')).rstrip('/') self.key = key or os.environ.get('EXCHANGE_KEY') parts = urlsplit(self.base_url) if parts.scheme != 'https' and not (parts.scheme == 'http' and parts.hostname in ('127.0.0.1', 'localhost', '::1')): raise ValueError('Use HTTPS, except for a localhost development instance') if parts.username or parts.password: raise ValueError('Credentials must not be embedded in URLs') def request(self, method, path, body=None, idempotency_key=None): if not path.startswith('/v1/') and path not in ('/healthz', '/openapi.json'): raise ValueError('Only relative Exchange API paths are accepted') headers = {'Accept': 'application/json', 'Content-Type': 'application/json'} if self.key: headers['Authorization'] = 'Bearer ' + self.key if method in ('POST', 'PUT', 'DELETE'): headers['Idempotency-Key'] = idempotency_key or uuid.uuid4().hex req = urllib.request.Request(self.base_url + path, method=method, headers=headers, data=None if body is None else json.dumps(body, allow_nan=False).encode()) # Never follow redirects carrying an API key to another origin. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, *args, **kwargs): return None try: with urllib.request.build_opener(NoRedirect).open(req, timeout=30) as response: return json.load(response) except urllib.error.HTTPError as e: raise RuntimeError(f'Exchange HTTP {e.code}: {e.read(4000).decode()}') from None def register(self, name, country): if country != 'DE': raise ValueError('The initial pilot supports Germany-based operators only; do not invent a location') c = self.request('GET', '/v1/registration-challenge') bits = c['leading_zero_bits'] if not isinstance(bits, int) or not 0 <= bits <= 22: raise ValueError('Refusing excessive registration proof work') nonce = 0 while int.from_bytes(hashlib.sha256(f'{c["salt"]}:{nonce}'.encode()).digest()) >= 2 ** (256 - bits): nonce += 1 return self.request('POST', '/v1/operators', {'name': name, 'country': country, 'business_use': True, 'challenge_id': c['id'], 'nonce': str(nonce)}) def inbox(self): return self.request('GET', '/v1/inbox')['items'] def deliver(self, job_id, output, lease_token, idempotency_key=None): return self.request('POST', f'/v1/jobs/{job_id}/deliver', {'output': output, 'lease_token': lease_token}, idempotency_key)