Files
codexpi/scripts/codexpi-remote
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

104 lines
4.3 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
from __future__ import annotations
import argparse
import json
import os
import sys
from codexpi.remote_ipc import remote_channel_request
from codexpi.remote_service import RemoteError, enqueue_remote_job
def main() -> int:
parser = argparse.ArgumentParser(description="Accès fichier limité à une machine associée à CodexPi")
parser.add_argument("--machine", help="identifiant de machine ; facultatif si une seule est associée")
subparsers = parser.add_subparsers(dest="operation", required=True)
subparsers.add_parser("machines")
listing = subparsers.add_parser("list")
listing.add_argument("path", nargs="?", default="")
reading = subparsers.add_parser("read")
reading.add_argument("path")
writing = subparsers.add_parser("write")
writing.add_argument("path")
writing.add_argument("--expected", help="SHA-256 obtenu lors de la lecture, pour empêcher un écrasement concurrent")
executing = subparsers.add_parser("exec")
executing.add_argument("argv", nargs=argparse.REMAINDER)
args = parser.parse_args()
database = os.environ.get("CODEXPI_DATABASE", "/app/data/codexpi.db")
remote_channel = os.environ.get("CODEXPI_REMOTE_CHANNEL")
project_id = os.environ.get("CODEXPI_PROJECT_ID", "")
if not project_id:
print("Cette commande doit être lancée depuis une session de projet CodexPi.", file=sys.stderr)
return 2
try:
if remote_channel:
payload = None
expected = None
if args.operation == "write":
if sys.stdin.isatty():
raise RemoteError("Le contenu du fichier doit être fourni sur l’entrée standard")
payload = {"content": sys.stdin.read()}
expected = args.expected
elif args.operation == "exec":
if not args.argv:
raise RemoteError("Indiquez la commande distante après exec")
payload = {"argv": args.argv, "timeout": 120}
result = remote_channel_request(remote_channel, {
"operation": args.operation,
"path": getattr(args, "path", ""),
"payload": payload,
"expected_hash": expected,
"machine_id": args.machine,
})
if args.operation == "machines":
print(json.dumps(result, ensure_ascii=False, indent=2))
return 0
elif args.operation == "machines":
import sqlite3
connection = sqlite3.connect(database)
connection.row_factory = sqlite3.Row
rows = connection.execute(
"SELECT id,name,folder_name,platform,last_seen FROM remote_machines WHERE project_id=? AND revoked_at IS NULL ORDER BY last_seen DESC",
(project_id,),
).fetchall()
print(json.dumps([dict(row) for row in rows], ensure_ascii=False, indent=2))
return 0
else:
payload = None
expected = None
if args.operation == "write":
if sys.stdin.isatty():
raise RemoteError("Le contenu du fichier doit être fourni sur l’entrée standard")
content = sys.stdin.read()
payload = {"content": content}
expected = args.expected
elif args.operation == "exec":
if not args.argv:
raise RemoteError("Indiquez la commande distante après exec")
payload = {"argv": args.argv, "timeout": 120}
result = enqueue_remote_job(
database,
project_id,
args.operation,
args.path,
payload,
expected,
args.machine,
)
if args.operation == "read":
sys.stdout.write(str(result.get("content", "")))
if result.get("content") and not str(result["content"]).endswith("\n"):
sys.stdout.write("\n")
print(f"[sha256:{result.get('sha256','')}]", file=sys.stderr)
else:
print(json.dumps(result, ensure_ascii=False, indent=2))
return 0
except (RemoteError, OSError, ValueError) as exc:
print(f"Erreur distante : {exc}", file=sys.stderr)
return 1
if __name__ == "__main__":
raise SystemExit(main())