Onyscribe Admin: Initialer Stand

Admin-Plattform für die Tenants von Onyscribe: Editor mit Live-Vorschau (echter Core in Sandbox), Versionen, KI-Agent (Cortecs),
OIDC-Abgleich über die Login-Developer-API, Deploy-Engine (Mittwald, gesperrt ohne DEPLOY_LIVE, Freigabe pro Tenant),
Core-Vertrag (Schema/Manifest), Core-Releases mit Kompatibilitätsprüfung, Canary-Rollout und Rollback, Zustandsabgleich.
Runbook: docs/RUNBOOK_PARALLELTEST.md

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
RubenRWU
2026-10-15 15:30:26 +02:00
commit 7d8c226da2
89 changed files with 13975 additions and 0 deletions

View File

@@ -0,0 +1,65 @@
"""Läuft in einem isolierten Verzeichnis mit Kopie des Cores + materialisiertem Tenant.
Aufruf: python preview_worker.py <index|pdf|validate> <ausgabedatei> (JSON-Eingabe auf stdin)
Führt den ECHTEN Core aus (auch overrides.py) – deshalb nur als Subprozess ohne Secrets/Netz-Keys.
"""
import json
import os
import sys
import traceback
sys.path.insert(0, os.getcwd())
kind, out = sys.argv[1], sys.argv[2]
args = json.loads(sys.stdin.read() or "{}")
def main() -> None:
from core import app as appmod
from core.config import CFG
if kind == "index":
page = appmod._render_index()
open(out, "w", encoding="utf-8").write(page)
elif kind == "pdf":
pdf = appmod.build_pdf(args.get("title") or "Vorschau", args["summary"], "SPRECHER_0: Beispiel.")
open(out, "wb").write(pdf)
elif kind == "prompt":
base = appmod._summary_prompt()
ctx = {"title": args.get("title", ""), "insurance_type": args.get("terminart", ""),
"customer_2": args.get("projekt", ""), "customer_name": args.get("kunde", ""), "customer_address": ""}
final = appmod.HOOKS.build_prompt(base, ctx)
json.dump({"prompt": final, "chars": len(final)}, open(out, "w"))
elif kind == "validate":
errors, warnings = [], []
page = appmod._render_index()
if "@@" in page:
errors.append("Web-UI: nicht ersetzte @@PLATZHALTER@@ im Template")
if not CFG.logo_path:
warnings.append("Kein Logo (logo.svg/logo.png) hinterlegt")
if not CFG.favicon_path:
warnings.append("Kein favicon.svg – es wird ein generisches Favicon verwendet")
try:
pdf = appmod.build_pdf("Validierung", args["summary"], "SPRECHER_0: Beispiel.")
if pdf[:5] != b"%PDF-":
errors.append("PDF-Rendering lieferte kein PDF")
except Exception as exc: # noqa: BLE001
errors.append(f"PDF-Rendering fehlgeschlagen: {exc}")
prompt = CFG.prompt_path.read_text(encoding="utf-8")
if len(prompt.strip()) < 200:
warnings.append("Prompt ist sehr kurz")
json.dump({"ok": not errors, "errors": errors, "warnings": warnings,
"tenant": CFG.id, "prompt_chars": len(prompt)}, open(out, "w"))
try:
main()
except SystemExit:
raise
except BaseException: # noqa: BLE001
tb = traceback.format_exc()
if kind == "validate":
json.dump({"ok": False, "errors": ["Tenant lässt sich nicht laden:\n" + tb[-1500:]], "warnings": []}, open(out, "w"))
else:
sys.stderr.write(tb)
sys.exit(2)