#!/usr/bin/env python3 """CLI `claude` fals pentru teste: citeste JSONL pe stdin, emite JSONL pe stdout. Scenariul se alege din FAKE_CLAUDE_SCENARIO: normal assistant + result pentru fiecare mesaj unknown tip necunoscut + linie non-JSON inainte de assistant/result tools tool_use + tool_result + text + result eof se termina inainte de `result` crash scrie pe stderr si iese cu cod 1 env raporteaza CLAUDE_DISCORD_THREAD_ID / _SESSION_ID din mediul propriu slow "lucreaza" FAKE_CLAUDE_DELAY secunde, colecteaza mesajele venite intre timp (asa se testeaza steering-ul mid-tur) si le enumera in `result` """ from __future__ import annotations import json import os import select import sys import time def emit(obj) -> None: sys.stdout.write(json.dumps(obj, ensure_ascii=False) + "\n") sys.stdout.flush() def parse_args(argv): sid, model = None, "sonnet" for i, a in enumerate(argv): if a == "--resume" and i + 1 < len(argv): sid = argv[i + 1] elif a == "--model" and i + 1 < len(argv): model = argv[i + 1] return sid, model def text_of(line: str) -> str: try: obj = json.loads(line) except ValueError: return line.strip() msg = obj.get("message") or {} content = msg.get("content") if isinstance(content, list): return "".join(b.get("text", "") for b in content if isinstance(b, dict)) return str(content or "") def assistant(sid: str, text: str) -> None: emit({"type": "assistant", "session_id": sid, "message": {"role": "assistant", "content": [{"type": "text", "text": text}]}}) def result(sid: str, text: str, ms: int = 10) -> None: emit({"type": "result", "subtype": "success", "session_id": sid, "is_error": False, "duration_ms": ms, "num_turns": 1, "result": text, "total_cost_usd": float(os.environ.get("FAKE_CLAUDE_COST", "0.0123"))}) def drain(deadline: float) -> list[str]: """Citeste orice mesaj suplimentar pana la deadline (steering mid-tur).""" extra = [] while True: left = deadline - time.monotonic() if left <= 0: return extra r, _, _ = select.select([sys.stdin], [], [], min(left, 0.05)) if r: line = sys.stdin.readline() if not line: return extra extra.append(text_of(line)) def main() -> int: scenario = os.environ.get("FAKE_CLAUDE_SCENARIO", "normal") sid, model = parse_args(sys.argv[1:]) sid = sid or os.environ.get("FAKE_CLAUDE_SID", "sid-fake-0001") emit({"type": "system", "subtype": "init", "session_id": sid, "model": model, "cwd": os.getcwd(), "tools": ["Bash", "Read"]}) if scenario == "crash": sys.stderr.write("fake_claude: boom, ies cu 1\n") sys.stderr.flush() return 1 for line in sys.stdin: prompt = text_of(line) if scenario == "unknown": emit({"type": "rate_limit_event", "session_id": sid, "detail": "test"}) sys.stdout.write("asta nu e JSON\n") sys.stdout.flush() if scenario == "tools": emit({"type": "assistant", "session_id": sid, "message": {"role": "assistant", "content": [ {"type": "tool_use", "id": "tu_1", "name": "Bash", "input": {"command": "ls"}}]}}) emit({"type": "user", "session_id": sid, "message": {"role": "user", "content": [ {"type": "tool_result", "tool_use_id": "tu_1", "content": "fisier1\n"}]}}) if scenario == "eof": assistant(sid, "incep si mor") return 0 if scenario == "slow": delay = float(os.environ.get("FAKE_CLAUDE_DELAY", "1.0")) assistant(sid, "lucrez...") extra = drain(time.monotonic() + delay) msgs = [prompt] + extra result(sid, "mesaje primite: " + " | ".join(msgs), int(delay * 1000)) continue if scenario == "env": assistant(sid, "raportez mediul") result(sid, "thread={} sesiune={} marker={}".format( os.environ.get("CLAUDE_DISCORD_THREAD_ID", "-"), os.environ.get("CLAUDE_DISCORD_SESSION_ID", "-"), os.environ.get("MARKER_DE_TEST", "-"))) continue assistant(sid, f"ecou: {prompt}") result(sid, f"ecou: {prompt}") return 0 if __name__ == "__main__": sys.exit(main())