Files
PaperClipAI/tests/runner-e2e/chat-campaign-preflight.py
T

61 lines
3.6 KiB
Python

"""Read-only campaign readiness. No case selection, database launch or IPC cleanup."""
import argparse
import datetime
import json
import os
import pathlib
import platform
import shutil
import subprocess
def command(arguments):
result = subprocess.run(arguments, capture_output=True, text=True, timeout=10)
return result.returncode, result.stdout
def inspect(root):
root = pathlib.Path(root).resolve()
ipc = {"available": False, "semaphore_headroom": None, "shared_memory_headroom": None}
if platform.system() == "Darwin":
status, output = command(["ipcs", "-s", "-a"])
status_m, output_m = command(["ipcs", "-m", "-a"])
status_limits, output_limits = command(["sysctl", "-n", "kern.sysv.semmns", "kern.sysv.shmmni"])
if status == status_m == status_limits == 0:
semaphores = [line.split() for line in output.splitlines() if line.startswith("s ")]
segments = [line for line in output_m.splitlines() if line.startswith("m ")]
limits = [int(value) for value in output_limits.split()]
consumed = sum(int(row[8]) for row in semaphores)
ipc = {"available": True, "semaphore_sets": len(semaphores), "semaphores_consumed": consumed,
"semaphore_limit": limits[0], "semaphore_headroom": limits[0] - consumed,
"shared_memory_segments": len(segments), "shared_memory_limit": limits[1],
"shared_memory_headroom": limits[1] - len(segments)}
runner = root / "packages/paperclip-runner/runner/target/debug/paperclip-runnerd"
tools = {name: bool(shutil.which(name)) for name in ["node", "pnpm", "python3", "codex"]}
credentials = {name: bool(os.getenv(name)) for name in ["CODEX_AUTH_JSON", "OPENAI_API_KEY", "ANTHROPIC_API_KEY", "DAYTONA_API_KEY"]}
status, revision = command(["git", "-C", str(root), "rev-parse", "HEAD"])
status_dirty, dirty = command(["git", "-C", str(root), "status", "--porcelain", "--untracked-files=no"])
# A guard against known exhaustion, not proof that a database will start.
ipc_guard = ipc["available"] and ipc["semaphore_headroom"] >= 34 and ipc["shared_memory_headroom"] >= 2
return {"version": 1, "measuredAt": datetime.datetime.now(datetime.timezone.utc).isoformat(),
"scope": "read-only host preflight; no selected-case attempt", "platform": platform.system(),
"sourceRevision": revision.strip() if status == 0 else None,
"trackedSourceClean": not dirty.strip() if status_dirty == 0 else None,
"tools": tools, "runnerExecutable": os.access(runner, os.X_OK),
"credentialsPresent": credentials,
"privateCodexAuthFilePresent": (pathlib.Path.home() / ".codex/auth.json").is_file(),
"localEnvironmentFilePresent": (root / ".env.runner-e2e.local").is_file(),
"immutableDaytonaImageConfigured": "@sha256:" in os.getenv("PAPERCLIP_E2E_DAYTONA_IMAGE", ""),
"ipc": ipc, "singleInstanceIpcGuardPassed": ipc_guard,
"ipcGuardLimits": {"minimumFreeSemaphores": 34, "minimumFreeSharedMemorySlots": 2},
"limitations": ["Credential existence does not establish account validity.",
"IPC headroom does not establish database or browser health.",
"No external connected integration is qualified by this inspection."]}
if __name__ == "__main__":
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--checkout", default=str(pathlib.Path(__file__).resolve().parents[2]))
arguments = parser.parse_args()
print(json.dumps(inspect(arguments.checkout), indent=2))