8772f7777b0c167e016b47f99c41e76aeecd17fd
11
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
255308f81b |
App-weite Warnung bei ungespeicherten Aenderungen + Options-UI-Fix + Cache-Busting
1. Generischer Ja/Nein-Dialog bei ungespeicherten Aenderungen (kein Opt-in pro Seite noetig): initUnsavedChangesGuard() erkennt automatisch jedes Formular mit echten Eingabefeldern, das seit dem Laden der Seite veraendert wurde, und fragt beim Klick auf einen Link (z.B. Sidebar- Navigation), ob zuerst gespeichert werden soll -- eigener Dialog (confirmSaveDiscard) statt des bestehenden confirmAction (dort ist "Abbrechen" = Bleiben, hier gibt es bewusst nur die zwei angefragten Optionen: speichern oder verwerfen, beide verlassen die Seite). "Ja, speichern" sendet das Formular ganz normal ab (echter POST) und merkt sich das eigentliche Ziel in sessionStorage, um danach automatisch dorthin weiterzuleiten. Nachtrag noch in dieser Session gefixt: ein normales Speichern ueber den Seiten-eigenen Button loeste zusaetzlich die native Browser-Warnung (beforeunload) aus, obwohl die Aenderung bereits gespeichert wurde -- navigatingAway-Flag wird jetzt bei jedem Submit eines beobachteten Formulars (eigener Button ODER eigener Ja/Nein-Dialog) gesetzt, die generische Browser-Warnung erscheint dadurch nur noch bei tatsaechlich ungefragter Navigation (Tab schliessen, Reload, Adresszeile). Live per Playwright in drei Szenarien verifiziert (direktes Speichern, Ja via eigenem Dialog, Nein via eigenem Dialog) -- kein Browser-Popup in den ersten beiden Faellen, korrektes Redirect zum urspruenglichen Ziel nach "Ja". 2. DHCP-Options-Dropdown (Reservierungs-Dialog) verschiebt ein neu ausgewaehltes Options-Feld jetzt direkt unter das Dropdown (in einen .poe-added-options-Container) statt es nur an seiner urspruenglichen Stelle in der bis zu 57 Eintraege langen Liste einzublenden -- lag dort oft ausserhalb des sichtbaren Modal-Ausschnitts und wirkte, als waere nichts passiert. Bereits gesetzte Overrides stehen von Anfang an im selben Container, weitere haengen sich beim Auswaehlen unten an. 3. Cache-Busting fuer app.js/style.css (asset_url()-Helper, haengt die Aenderungszeit der Datei als ?v=... an) -- ein im Browser gecachtes, veraltetes app.js konnte nach einem Update sonst den Eindruck erwecken, eine neue Funktion wuerde "nicht funktionieren", obwohl der Code auf dem Server laengst aktuell war. Alle drei live auf der Test-VM deployt und per Playwright verifiziert, keine Fehler im journalctl-Log. (Testartefakt aufgeraeumt: der echte Host-Hostname wurde durch die Playwright-Tests versehentlich auf "directsave-test"/"navsave-test" geaendert und wieder auf POETEST zurueckgesetzt.) |
||
|
|
1a0b55ed44 |
DHCP: manuelle Reservierungen, Options-Schutz+Dropdown-UI, Lease-Countdown-Fix
1. Manuelle Reservierungen (dhcp_manual_reservations) fuer Geraete, die nicht als Client in dieser App gepflegt werden -- bewusst eine eigene, explizite Aktion (MAC/IP/Name), damit kein externes Geraet unueberwacht automatisch eine feste IP bekommt. _dhcp_reservation_candidates fuehrt automatische (aus devices) und manuelle Reservierungen jetzt zusammen, case-insensitiv nach MAC dedupliziert (automatisch gewinnt bei Konflikt). Automatische Reservierungen sind in der Tabelle nicht loeschbar (Schloss-Symbol statt Button, sie entstehen/verschwinden mit den Client-Stammdaten), manuelle schon. Live verifiziert: Hinzufuegen, doppelte MAC ablehnen, ungueltige MAC ablehnen, generierte Kea-Config mit gemischten Auto-/manuellen Reservierungen besteht kea-dhcp4 -t. 2. Standard-DHCP-Optionen um 10 weitere verifizierte Optionen erweitert (u.a. host-name/12 -- Kea-Name mit Bindestrich, NICHT "hostname", live gegen echtes Kea geprueft) -- jetzt 57 insgesamt. Standard-Optionen sind nicht mehr loeschbar (Backend lehnt es ab, UI zeigt ein Schloss-Symbol statt Loeschen-Button) -- vorher liess sich eine vorbefuellte Options- Definition versehentlich entfernen. 3. Options-Tabellen (global UND pro Reservierung) zeigen jetzt nur noch tatsaechlich genutzte Options -- eigene immer, Standard-Optionen nur mit gesetztem Wert. Weitere Standard-Optionen kommen ueber ein Dropdown "+ Option hinzufuegen" dazu statt permanent alle 57 als leere Felder zu zeigen. Das neu eingeblendete Feld wird automatisch in den sichtbaren Bereich gescrollt und fokussiert (bei bis zu 57 moeglichen Optionen kann das Feld sonst weit unterhalb des sichtbaren Modal-Ausschnitts liegen und es wirkt so, als waere nichts passiert). 4. Lease-Countdown-Fix: die Ablaufzeit wird jetzt explizit als UTC ausgewiesen (Kea arbeitet intern in UTC-Epoch) und zusaetzlich als live tickender Countdown angezeigt (gleiches Prinzip wie der bestehende "Naechste Pruefung"-Timer). Vorher wirkte eine reine Absolutzeit auf der nicht selbst aktualisierenden Seite bei den kurzen Standard-Lease-Zeiten (600s) schnell "schon abgelaufen", sobald etwas Zeit vergangen war, bis man tatsaechlich hinschaut -- live im echten Betrieb genau als dieses Missverstaendnis beobachtet und anhand der rohen Kea-CSV verifiziert, dass die richtige Spalte (expire) korrekt gelesen wird. 5. UI-Feinschliff: "In Datei schreiben" bei den Reservierungen heisst jetzt "Speichern" und ist der primaere (orange) Button, "Reservierung hinzufuegen" sekundaer -- das Hinzufuegen legt nur einen Datensatz an, erst "Speichern" schreibt die tatsaechlich wirksame Kea-Config. Alles live auf der Test-VM deployt und verifiziert (Playwright fuer die Dropdown-Sichtbarkeit/Fokus-Interaktion, curl fuer die Backend-Validierung, kea-dhcp4 -t fuer die generierte Konfiguration), keine Fehler im journalctl-Log. |
||
|
|
182457629d |
Fix: 'Jetzt pruefen' aktualisierte Topbar-Countdown nur auf dem Dashboard
Der Klick loeste den Neustart von rpi-check.service und damit einen echten, sofortigen Pruef-Durchlauf immer korrekt aus -- nur die Topbar-Anzeige 'Naechste Pruefung in Xs' wurde ausserhalb des Dashboards nie aktualisiert, da nur index.html einen Listener fuer das poe:check-triggered-Event registrierte. Auf allen anderen Seiten (DHCP, Switches, Settings, ...) lief der Countdown deshalb sichtbar mit dem alten Zeitpunkt weiter, obwohl im Hintergrund laengst neu geprueft wurde. Fix (klein gehalten, kein neuer Endpunkt): run_check_now() wartet kurz auf den ersten Log-Eintrag des frisch gestarteten Dienstes und liefert den neuen Zeitpunkt direkt in seiner bestehenden JSON-Antwort mit. app.js schreibt diesen Wert danach unabhaengig von der aktuellen Seite ins global-timer-pill-Element -- der laufende Countdown liest seine Werte ohnehin bei jedem Tick frisch aus dem DOM. Live auf der Test-VM verifiziert (mehrere echte Durchlaeufe ueber /check/run-now, frischer last_run_epoch_ms in der Antwort, keine Fehler im journalctl-Log). |
||
|
|
4d7433e832 |
Fix: SSH-Terminal-Login (No auth methods), kaputtes DOM in Bearbeiten-Modals
Quelltext-Huerde wieder entfernt (auf Wunsch) - siehe vorherigen Commit,
hier nur der Revert von initSourceProtection() und der README-Erwaehnung.
SSH-Terminal-Login war komplett kaputt (von dir gemeldet + Screenshot):
- Root Cause: das Init-Payload vom Browser enthielt nie ein Passwort,
SSHClient.connect() bekam also weder Passwort noch Key noch Agent und
scheiterte sofort mit "No authentication methods available" - noch
bevor ueberhaupt eine interaktive Passwortabfrage moeglich gewesen
waere (die High-Level-API erledigt Host-Key-Pruefung UND
Authentifizierung in einem blockierenden Aufruf).
- Fix: Umstieg auf die Low-Level paramiko.Transport-API. Nach
Host-Key-Bestaetigung wird aktiv erfragt, welche Auth-Methoden der
Server anbietet (auth_none), und bei Bedarf interaktiv ueber das
Browser-Terminal nach Passwort/keyboard-interactive-Prompts gefragt
-- genau das Verhalten, das der bestehende Hinweistext im Modal schon
immer versprach, aber nie tatsaechlich implementiert war.
Host-Key-Verifikation dabei manuell nachgebaut (_verify_host_key_interactive)
inkl. hartem Ablehnen bei GEAENDERTEM (nicht nur unbekanntem) Host-Key,
wie ein echtes ssh-CLI bei einer moeglichen MITM-Situation.
- Waehrend der Live-Verifikation gegen ein echtes Geraet zwei weitere
Bugs gefunden und gefixt: ws.receive() wirft in diesem Setup
ConnectionClosed statt None zurueckzugeben (crashte
_terminal_read_line unbehandelt -> "Invalid frame header" beim
Client); _send_and_close() crashte ebenso, wenn der Client bereits weg
war. Beide jetzt defensiv abgefangen.
- Live gegen ein echtes Zielgeraet verifiziert (Host-Key-Bestaetigung,
Passwort-Prompt, erfolgreicher Login) sowie manuell von dir bestaetigt.
Kaputtes DOM in zwei Bearbeiten-Modals (von dir gemeldet: "Verbindung
testen" oeffnete beim Switch bearbeiten kein Fenster, obwohl es beim
Neuanlegen funktionierte):
- Root Cause: <div class="modal-overlay">...</div> stand direkt in
<tbody> (nur <tr> ist dort gueltig). Browser "foster-parenten"
ungueltigen Tbody-Inhalt aus der Tabelle heraus und zerreissen dabei
teils die Eltern-Kind-Beziehung zwischen <form> und seinen Buttons --
this.closest("form") lieferte dadurch null statt des Formulars.
Betroffen: editSwitchModal (switches.html), deviceOptionsModal
(settings_dhcp.html). Fix: beide Modal-Bloecke aus der Tabelle heraus
in eine eigene Schleife direkt danach verschoben (gleiches Muster wie
die bereits korrekten Neuanlegen-Modals).
- Per DOM-Inspektion verifiziert: this.closest("form") lieferte vorher
null, danach das korrekte Formular fuer alle Zeilen; End-to-End-Test
bestaetigt, dass sich das Terminal-Modal jetzt oeffnet.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
||
|
|
6803929140 |
Hostname-Einstellung, DHCP-Range-Pflicht, Topbar-Ampel, Text-Straffung, Quelltext-Huerde
Hostname (Systemeinstellungen, neben Pruefintervall): - hostnamectl set-hostname ueber die App, Validierung (RFC-1123-artiges Label), kein Revert-Timer noetig (kappt die Erreichbarkeit nicht wie eine IP-Aenderung). Live getestet inkl. Validierung und Revert. DHCP: Range ist jetzt Pflicht, kein geratener Default mehr: - dhcp_range_start/end/dns starten leer (Platzhaltertext statt fake- echt aussehendem Default) - ein Zufalls-Range haette den Dienst sonst unbemerkt mit einer zum echten Netz nicht passenden Konfiguration starten lassen koennen. - write_dhcp_file und dhcp_service_action=enable_restart verweigern sich ohne eingetragene Range; _render_kea_config laesst "pools" ohne Range komplett weg statt einen kaputten Pool-String zu erzeugen. - Neues, separates dhcp_gateway-Feld (optional) fuer einen vom automatisch erkannten Gateway abweichenden Router fuer die Clients. Topbar-Ampel fuer den Kea-Dienst: - Gruener/roter Punkt neben dem Pruefintervall-Timer, nur sichtbar mit settings_dhcp.view (ein einzelner, kurzer systemctl-Aufruf pro Request, nicht die volle Status-Erkennung). Live verifiziert (rot wenn gestoppt, gruen wenn gestartet). Reservierungstabelle zeigt jetzt pro Client, welche eigene DHCP-Option greift (global oder Client-Override, mit Wert im Tooltip). Text-Straffung: die laengsten Hint-Texte und Code-Kommentare in Templates/app.py gekuerzt (u.a. groups.html, settings_dhcp.html, account.html, devices/switches/users/credentials.html, zwei grosse Migrations-/DHCP-Kommentarbloecke in app.py) - Kernaussagen erhalten, Redundanz entfernt. Quelltext-Huerde (KEINE echte Sicherheit, nur Abschreckung): Rechtsklick und DevTools-/Quelltext-Shortcuts per JS blockiert. Klar dokumentiert in Kommentar + README, dass der Browser HTML/CSS/JS immer vollstaendig ausliefert und das in Sekunden umgehbar ist - echte Absicherung bleiben ausschliesslich die serverseitigen Rechteprüfungen. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
7d7239e1af |
Fix: Benutzer-Löschen im Browser kaputt (JS), Gruppen-Rechtetabelle überarbeitet
Bugfix (von dir gemeldet, live auf Port 5000 gefunden): - initConfirmables() in app.js rief beim bestätigten Absenden form.requestSubmit() OHNE den ursprünglichen Submitter auf. Laut Spec wird dann KEIN Submit-Button-Name/Value mitgeschickt. users.html trug die Benutzer-ID als name/value des Löschen-Buttons selbst (statt eines verstecken Inputs wie überall sonst) — dadurch kam "delete_user" nie im Request an, der Klick tat scheinbar nichts. Fix: e.submitter beim ersten Klick merken und an requestSubmit(submitter) weiterreichen (behebt es grundsätzlich für alle Formulare dieser Art); zusätzlich users.html auf das robustere hidden-input-Muster umgestellt wie credentials/devices/groups. Mit echtem Playwright-Browserklick (nicht nur curl) verifiziert. Gruppen-Rechtetabelle, mehrere Korrekturrunden nach deinem Mockup: - Bereichsname + Kill-Switch-Kästchen jetzt eng nebeneinander in der Tabellen-Kopfzeile (nicht mehr an den Rand gestellt), R/W/E/D-Spalten in derselben Zeile statt einer zweiten. - Nur die von einem Bereich tatsächlich genutzten Spalten werden gezeigt (GROUP_ROW_TYPES) — Logs zeigt nur R, Einstellungen nur R/W/E, nur Geräte zeigt zusätzlich D. - Bugfix: D-Spalte bei Geräte verschwand durch zu schmale Flex-Breite der Spalte im Overflow, obwohl serverseitig korrekt gerendert — behoben durch flex:0 0 auto statt fester Breite. - Checkbox-Zentrierung robust per display:block+margin:auto statt sich auf native Checkbox-Ränder zu verlassen (waren leicht asymmetrisch). - Unterpunkte deutlich weiter eingerückt (34px) für klare Hierarchie. - Sichtbares Ausgrauen (nicht nur natives disabled) der Unterpunkte, solange der Bereich-Kill-Switch aus ist (JS setzt .permission-locked). - Legende jetzt UNTER der Rechtetabelle statt darüber. - Rechte lassen sich jetzt direkt beim Anlegen einer neuen Gruppe setzen (Modal zeigt dieselbe Rechtetabelle, add_group-Route verarbeitet mitgesendete permissions[] im selben Request) statt sie erst danach über die Gruppentabelle nachzupflegen. Alles inkl. Erstellen-mit-Rechten und Bearbeiten-Regression live per Playwright verifiziert. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
c4f682b5de |
UI-Fixes (Button-Wrap, Suchfeld-Icon, Log-Farben, Nav-Persistenz), manueller Check-Trigger, echter Live-Refresh
- section-head: Button ("+ Neue Zugangsdaten" etc.) rutscht bei langer
Beschreibung nicht mehr in die nächste Zeile — bleibt immer rechts oben,
der Beschreibungstext wickelt stattdessen innerhalb seiner eigenen Spalte.
- Suchfeld-Icon überlappte den Platzhaltertext: eine spätere, generische
Input-Regel mit gleicher Spezifität hat die padding-left-Regel des
Lupe-Icons überschrieben (reine Reihenfolge-im-Stylesheet-Sache) — Selektor
spezifischer gemacht.
- Live-Log: Neustart-bezogene Zeilen (manueller Neustart, automatischer
PoE-Restart bei Ausfall) werden jetzt orange markiert statt farblos.
- Aufgeklappte Nav-Gruppen bleiben über Seitenwechsel hinweg erhalten
(localStorage), zusätzlich zur automatischen Aufklappung der Gruppe der
aktuell aktiven Seite.
- Datei-Auswahl (Import) im Dark Mode gestylt (Text + Button), inkl.
color-scheme-bewusster Browser-Widgets.
- Manueller "Jetzt prüfen"-Trigger (Icon neben dem globalen Timer, nur
Admins): startet rpi-check.service neu -> frisches Logfile + sofortiger
Durchlauf, Countdown synchronisiert sich auf den manuellen Zeitpunkt.
- Dashboard und Live-Log aktualisieren sich jetzt per AJAX im Hintergrund
(kein voller Seiten-Reload mehr): neue Route /dashboard/tiles liefert das
Kachel-Fragment (_dashboard_tiles.html, von index.html eingebunden und vom
Live-Update nachgeladen), get_log() liefert den aktuellen Dateinamen als
Header mit, damit die Anzeige nach einem Service-Neustart aktuell bleibt.
- Root-Cause für "Intervall bleibt immer bei 300s": in der WSL-Testumgebung
lief poe_wrapper.py nur als manuell gestarteter Hintergrundprozess statt
als echter rpi-check.service — "systemctl restart" lief dadurch ins Leere.
Echten systemd-Service in WSL eingerichtet und Intervalländerung end-to-end
verifiziert (Log-Zeilenabstand folgt jetzt dem neu gesetzten Intervall).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
||
|
|
0fcb6c94cb |
Topbar auf globalen Prüf-Timer reduziert, Aktionen+Beschreibung über Tabellen, sortierbare Listen
- Topbar zeigt rechts jetzt ausschließlich den "Nächste Prüfung"-Countdown, konsistent auf jeder Seite (auch anonymes Dashboard) statt nur auf dem Dashboard. Neuer Context-Processor inject_check_timer()/get_last_run_at() liefert last_run/interval global, ohne dass jede Route das selbst berechnen muss. - "+ Neu ..."-Buttons (Devices, Switches, Zugangsdaten, Benutzer, Gruppen) aus der Topbar entfernt und stattdessen in einen .section-head direkt über der jeweiligen Tabelle verschoben, zusammen mit einer kurzen Beschreibung der Seite (bisher nur bei Zugangsdaten/Gruppen vorhanden, jetzt auch bei Geräte/Switche/Benutzer). - Live-Log: eigene lokale Timer-Pill entfernt (redundant zum globalen Timer), "Aktualisieren"-Button in denselben section-head verschoben. Dashboard: Suchfeld aus der Topbar in den Seiteninhalt verschoben, lokale Timer-Anzeige entfernt (übernimmt die globale Topbar-Pill), Reload-bei- Intervallende-Logik bleibt als separater, unsichtbarer Scheduler erhalten. - Neue generische Tabellen-Sortierung (app.js: initSortableTables): Klick auf eine Spaltenüberschrift mit data-sort-key sortiert die Zeilen anhand von data-sort-<key>-Attributen. Unterstützt auch Akkordeon-Tabellen mit mehreren <tbody> (Gruppen: Haupt- + Detail-Zeile bleiben als Einheit zusammen, die virtuelle "Admin"-Zeile bleibt über data-sort-pinned immer oben). Angewendet auf Geräte, Switche, Zugangsdaten, Benutzer, Gruppen. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
a7a23b7c12 |
Audit-Log, Import/Export, eigenes Profil & anpassbare Navigation
- Änderungslog: audit_log-Tabelle + last_modified_by/at an Devices und Switches; log_action()/touch_record() in allen Verwaltungsrouten (Devices, Switches, Zugangsdaten, Benutzer, Gruppen, Settings) verdrahtet. Neue Admin-Seite "Änderungslog" unter /logs/aenderungen. PoE-Neustarts werden bewusst nicht geloggt. - Verschlüsseltes Import/Export für Devices/Switches/Zugangsdaten unter Settings, passphrasenbasiert (PBKDF2 + Fernet), für Umzug auf neue Umgebungen. Referenziert Zugangsdaten/Switche über Name/Hostname statt interner ID für stabilen Re-Import. - Eigenes Profil: Klick auf den Namen in der Sidebar öffnet ein Modal zum Ändern von Vor-/Nachname, eigenem Passwort (mit Prüfung des aktuellen Passworts) und Profilbild-Upload (Anzeige in Sidebar + Änderungslog). - Anpassbare Navigation: Reihenfolge der Sidebar-Punkte ist unter Settings per Auf-/Ab-Buttons konfigurierbar (settings.nav_order); "Devices"/ "Users"/"Settings" umbenannt zu "Geräte"/"Benutzer"/"Einstellungen"; "Live-Log" und "Änderungslog" zu aufklappbarer "Logs"-Gruppe zusammengefasst. Jeder Benutzer sieht weiterhin nur, wofür er berechtigt ist. - create_db.py mit allen Schema-Erweiterungen synchronisiert (audit_log, last_modified_by/at, avatar_filename) für Frischinstallationen. - venv-Umgebung im WSL neu aufgesetzt (war fälschlich unter dem Erstellungspfad venv-linux verankert und daher nicht aktivierbar). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
fa6bb2935f |
Zugangsdaten-Entitaet, Gruppen-Tabelle mit Aufklappansicht, Vor-/Nachname, Dashboard-Sektionen
- Zugangsdaten (SSH-Logins) als eigenstaendige, wiederverwendbare Entitaet statt direkt am Switch; inline Neuanlage beim Switch-Erstellen moeglich; automatische Migration bestehender Switch-Logins - Gruppen-Seite als Tabelle mit Aufklapp-Zeile fuer Rechte (Akkordeon), Admin als feste Systemzeile (Mitgliederverwaltung ueber is_admin), Standardgruppe 'Benutzer' mit allen Ansichtsrechten (devices.view, switches.view), automatische Zuordnung neuer/verwaister Benutzer - Users-Seite: Icon-Buttons statt Text, Bearbeiten+Passwortaenderung in einem Modal zusammengefuehrt, Gruppe/Admin-Zuweisung ueber eigenen Zuweisen-Button (wie Switch-Zuordnung bei Devices), Vor-/Nachname - Dashboard: einheitliche Kachelansicht mit/ohne Login, drei sortierte Abschnitte (Offline/Online/Deaktiviert), Kachel-Suchfilter, Bootstrap- artiges Grid (max. 6 Spalten), Aktivieren-Option im Popup fuer deaktivierte Geraete, Countdown serverseitig korrekt geseedet - Sidebar dauerhaft einklappbar (Desktop, persistent via localStorage) - Devices-Tabelle: Aktions-Buttons nebeneinander statt untereinander Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
82bfeb17ed |
Initial commit: PoE Manager modern UI rebuild
- Neues, eigenständiges Frontend (Sidebar, zentriertes Logo in der Topbar, Dark/Light-Theme, Karten-Dashboard, Toasts/Modals statt Bootstrap) - Oeffentliches Kurz-Dashboard ohne Login (Status-Uebersicht) - Browser-SSH-Terminal (paramiko, plattformunabhaengig) zum Testen von Switch-Zugangsdaten inkl. interaktiver Host-Key-Bestaetigung - Granulares Rechtesystem mit Gruppen (Devices/Switches-Berechtigungen) - Aufgeraeumtes Backend mit konfigurierbaren Pfaden, auto-generierten Secrets statt hart codierter Werte im Original Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |