feat: publish CodexPi contest snapshot
Some checks are pending
Qualité / tests (push) Waiting to run
Some checks are pending
Qualité / tests (push) Waiting to run
This commit is contained in:
86
scripts/codexpi_host_agent.py
Normal file
86
scripts/codexpi_host_agent.py
Normal file
@@ -0,0 +1,86 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Private Unix-socket bridge for owner-approved CodexPi master sessions."""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import hmac
|
||||
import json
|
||||
import os
|
||||
import socket
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def receive(connection: socket.socket, limit: int = 100_000) -> dict:
|
||||
chunks, size = [], 0
|
||||
while True:
|
||||
block = connection.recv(8192)
|
||||
if not block:
|
||||
break
|
||||
size += len(block)
|
||||
if size > limit:
|
||||
raise ValueError("request too large")
|
||||
chunks.append(block)
|
||||
if b"\n" in block:
|
||||
break
|
||||
return json.loads(b"".join(chunks).split(b"\n", 1)[0])
|
||||
|
||||
|
||||
def run_agent(codex: str, prompt: str, privileged: bool) -> tuple[bool, int, str]:
|
||||
rules = (
|
||||
"Tu es l’agent maître CodexPi sur l’hôte réel. Vérifie la cible avant toute écriture, protège les secrets, "
|
||||
"utilise des commandes structurées, évite toute action destructive non explicitement demandée et rends compte des vérifications. "
|
||||
"Les privilèges disponibles proviennent uniquement de l’identité de ce service et de la politique sudo de l’hôte."
|
||||
)
|
||||
if privileged:
|
||||
rules += " Cette demande a reçu la confirmation privilégiée du propriétaire."
|
||||
command = [codex, "--dangerously-bypass-approvals-and-sandbox", "exec", "--json", "--skip-git-repo-check", rules + "\n\nDEMANDE DU PROPRIÉTAIRE\n" + prompt]
|
||||
process = subprocess.run(command, cwd="/", text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, timeout=3600, check=False)
|
||||
answers = []
|
||||
for line in process.stdout.splitlines():
|
||||
try:
|
||||
event = json.loads(line)
|
||||
except ValueError:
|
||||
continue
|
||||
item = event.get("item") or {}
|
||||
text = item.get("text") if item.get("type") in {"agent_message", "assistant_message"} else None
|
||||
if text:
|
||||
answers.append(str(text))
|
||||
answer = answers[-1] if answers else "L’agent maître n’a produit aucun compte rendu exploitable."
|
||||
return process.returncode == 0, process.returncode, answer
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--socket", required=True)
|
||||
parser.add_argument("--token-file", required=True)
|
||||
parser.add_argument("--codex", default="codex")
|
||||
args = parser.parse_args()
|
||||
socket_path, token_path = Path(args.socket), Path(args.token_file)
|
||||
token = token_path.read_text(encoding="utf-8").strip()
|
||||
if len(token) < 32 or token_path.stat().st_mode & 0o077:
|
||||
raise SystemExit("unsafe host-agent token")
|
||||
socket_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
if socket_path.exists():
|
||||
socket_path.unlink()
|
||||
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as server:
|
||||
server.bind(str(socket_path))
|
||||
os.chmod(socket_path, 0o660)
|
||||
server.listen(4)
|
||||
while True:
|
||||
connection, _ = server.accept()
|
||||
with connection:
|
||||
try:
|
||||
request = receive(connection)
|
||||
if not hmac.compare_digest(str(request.get("token") or ""), token):
|
||||
raise PermissionError("unauthorized")
|
||||
ok, code, answer = run_agent(args.codex, str(request.get("prompt") or ""), bool(request.get("privileged")))
|
||||
response = {"ok": ok, "exit_code": code, "answer": answer}
|
||||
except Exception:
|
||||
response = {"ok": False, "exit_code": 1, "answer": "Le canal hôte a refusé ou interrompu la demande."}
|
||||
connection.sendall(json.dumps(response, ensure_ascii=False).encode() + b"\n")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user