"""One-shot ops script: register the agenteval-patrol cron job on the OpenClaw gateway via its WS RPC protocol, authenticating through the trusted-proxy network (x-forwarded-user). Run inside the agenteval container.""" import base64 import json import os import socket import struct import sys HOST, PORT = "openclaw-eval", 18789 def recv_exact(s, n): buf = b"" while len(buf) < n: chunk = s.recv(n - len(buf)) if not chunk: raise ConnectionError("closed") buf += chunk return buf def read_frame(s): b1, b2 = recv_exact(s, 2) length = b2 & 0x7F if length == 126: length = struct.unpack(">H", recv_exact(s, 2))[0] elif length == 127: length = struct.unpack(">Q", recv_exact(s, 8))[0] return recv_exact(s, length).decode() def send_text(s, payload): data = payload.encode() mask = os.urandom(4) header = b"\x81" n = len(data) if n < 126: header += bytes([0x80 | n]) elif n < 65536: header += bytes([0x80 | 126]) + struct.pack(">H", n) else: header += bytes([0x80 | 127]) + struct.pack(">Q", n) masked = bytes(d ^ mask[i % 4] for i, d in enumerate(data)) s.sendall(header + mask + masked) def rpc(s, rid, method, params): send_text(s, json.dumps({"type": "req", "id": rid, "method": method, "params": params})) while True: msg = json.loads(read_frame(s)) if msg.get("type") == "res" and msg.get("id") == rid: return msg print("note:", json.dumps(msg)[:200]) def main(action): s = socket.create_connection((HOST, PORT), timeout=15) key = base64.b64encode(os.urandom(16)).decode() req = ( f"GET / HTTP/1.1\r\nHost: {HOST}:{PORT}\r\nUpgrade: websocket\r\n" f"Connection: Upgrade\r\nSec-WebSocket-Key: {key}\r\nSec-WebSocket-Version: 13\r\n" f"Origin: http://192.168.8.145:8001\r\n" f"x-forwarded-user: agenteval\r\n\r\n" ) s.sendall(req.encode()) resp = b"" while b"\r\n\r\n" not in resp: resp += s.recv(4096) status = resp.split(b"\r\n")[0].decode() print("UPGRADE:", status) if "101" not in status: sys.exit(1) challenge = json.loads(read_frame(s)) print("CHALLENGE:", json.dumps(challenge)[:300]) connect_params = { "minProtocol": 4, "maxProtocol": 4, "client": {"id": "openclaw-control-ui", "version": "0.0.1", "platform": "linux", "mode": "ui"}, "role": "operator", "scopes": ["operator.read", "operator.write", "operator.admin"], "caps": [], "commands": [], "permissions": {}, "auth": {}, "locale": "zh-CN", "userAgent": "agenteval-ops/0.0.1", } res = rpc(s, "c1", "connect", connect_params) print("CONNECT:", json.dumps(res)[:400]) if not res.get("ok"): sys.exit(2) if action == "list": res = rpc(s, "c2", "cron.list", {"includeDisabled": True}) print("CRON.LIST:", json.dumps(res, ensure_ascii=False)[:1200]) elif action == "add": params = { "name": "agenteval-patrol", "description": "AgentEvalTool 常驻巡检:每小时按 agenteval-patrol skill 闭环巡检探索", "enabled": True, "agentId": "main", "schedule": {"kind": "cron", "expr": "0 * * * *", "tz": "Asia/Shanghai"}, "sessionTarget": "isolated", "wakeMode": "now", "payload": { "kind": "agentTurn", "message": ( "使用 agenteval-patrol skill 执行一次完整巡检闭环:调用巡检 API," "研判新结果与预算余量,按规则派发/驱动探索会话并提交体验记录," "收到 409 立即收敛,最后汇报本轮巡检结果。" ), }, "delivery": {"mode": "none"}, } res = rpc(s, "c2", "cron.add", params) print("CRON.ADD:", json.dumps(res, ensure_ascii=False)[:1200]) elif action == "run": job_id = sys.argv[2] res = rpc(s, "c2", "cron.run", {"id": job_id}) print("CRON.RUN:", json.dumps(res, ensure_ascii=False)[:1200]) elif action == "runs": job_id = sys.argv[2] res = rpc(s, "c2", "cron.runs", {"id": job_id}) print("CRON.RUNS:", json.dumps(res, ensure_ascii=False)[:2000]) s.close() if __name__ == "__main__": main(sys.argv[1] if len(sys.argv) > 1 else "list")