Files
ROMFASTSQL/proxmox/lxc171-claude-agent/discord-bridge/tests/fake_claude.py
Claude Agent d466f358ce feat(discord-bridge): punte Discord -> Claude Code pe LXC 171
Implementeaza planul claude-master-plan-discord-bridge-20260830 (15 taskuri,
3 lane-uri paralele) — un bot subtire discord.py peste CLI-ul `claude`, cu
proces persistent per fir alimentat pe stdin cu --input-format stream-json.

Nucleu: runner (proces persistent + reaper 20min + respawn --resume), stream
(parser tolerant), session_store (scriere atomica, lock per fir, detectare PID
reuse, recovery), limits (max 4 procese, timeout tur, rate per user, plafon cost
pe zi), render (un loop de editare per canal, interval adaptiv).

Adaptor: allowlist guild/canal/user fail-closed cu respingerea webhook-urilor,
comenzi !new/!cd/!model/!status/!stop/!cleanup, cost si model in subsolul
fiecarui raspuns. Mesajul sosit in timpul unui tur devine steering, nu tur nou.

Securitate: hook PreToolUse fail-closed care cere confirmare in Discord pentru
operatiuni ireversibile, wrapper `infra` cu lista explicita de hosturi. Deny
rules raman strat cosmetic, nu bariera (verificat: /usr/bin/ssh trece pe langa).

Ops: alerte email pe conventia repo-ului, !cleanup pentru orfani, unit systemd
user cu KillMode=control-group si limite de memorie, install.sh idempotent.

Verificat: 275 teste fara retea/Discord/API (10.8s), identic cu si fara
discord.py instalat; e2e pe CLI real confirma steering-ul mid-tur (mesaj la 6s
intr-un tool call de 25s schimba raspunsul final).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B29CApsP1JkSdjYaGaHpE7
2026-08-30 10:44:39 +00:00

123 lines
4.4 KiB
Python
Executable File

#!/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())