native-computer.py 2.3 KB

123456789101112131415161718192021222324252627282930
  1. """Disposable UI fixture; use distinct ports for concurrent native clients."""
  2. import argparse, base64, json, time, threading
  3. from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
  4. from pathlib import Path
  5. parser=argparse.ArgumentParser();parser.add_argument('--port',type=int,required=True);parser.add_argument('--image',required=True);args=parser.parse_args()
  6. image=base64.b64encode(Path(args.image).read_bytes()).decode()
  7. state={'mode':'agent','generation':1,'controlId':None,'actions':[],'notes':''}
  8. lock=threading.Lock()
  9. class Handler(BaseHTTPRequestHandler):
  10. def log_message(self,*args): pass
  11. def respond(self,value):
  12. data=json.dumps(value,ensure_ascii=False).encode();self.send_response(200);self.send_header('Content-Type','application/json');self.send_header('Content-Length',str(len(data)));self.end_headers();self.wfile.write(data)
  13. def do_GET(self):
  14. with lock:
  15. if self.path=='/fixture/state': return self.respond(state)
  16. if self.path.endswith('/frame'): return self.respond({'id':'22222222-2222-4222-8222-222222222222','generation':state['generation'],'data':image,'width':512,'height':512,'capturedAt':int(time.time()*1000)})
  17. if self.path.endswith('/computer'): return self.respond({**state,'canResume':True})
  18. return self.respond({'ok':True,'csrfToken':'fixture','features':['computerControl']})
  19. def do_POST(self):
  20. data=json.loads(self.rfile.read(int(self.headers.get('Content-Length','0'))) or b'{}')
  21. with lock:
  22. if self.path=='/fixture/reset': state.update(mode='agent',generation=1,controlId=None,actions=[],notes='')
  23. elif self.path.endswith('/take'):
  24. state.update(mode='human',generation=state['generation']+1,controlId='11111111-1111-4111-8111-111111111111')
  25. return self.respond({**state,'token':'fixture-private-control-token-1234567890','canResume':True})
  26. elif self.path.endswith('/input'): state['actions'].append(data.get('action'))
  27. elif self.path.endswith('/giveback'): state.update(mode='agent',generation=state['generation']+1,controlId=None,notes=data.get('notes',''))
  28. return self.respond({'ok':True})
  29. print(f'native computer fixture on 127.0.0.1:{args.port}',flush=True)
  30. ThreadingHTTPServer(('127.0.0.1',args.port),Handler).serve_forever()