Files
codexpi/scripts/codexpi_host_agent.py
CodexPi 995b17a6e2
Some checks are pending
Qualité / tests (push) Waiting to run
feat: publish CodexPi contest snapshot
2026-11-14 20:23:59 +01:00

87 lines
3.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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())