18 KiB
SwyxTray – Leitfaden für Coding-Agenten
Projektüberblick
SwyxTray ist eine Windows-Desktop-Anwendung ohne Hauptfenster. Sie läuft als normale Benutzeranwendung in derselben interaktiven Windows-Sitzung wie SwyxIt!, verbindet sich über das Swyx Client SDK mit dem bereits laufenden Client Line Manager (CLMgr.exe) und zeigt den Telefoniezustand als farbiges Symbol im Infobereich an.
Zusätzlich stellt die Anwendung einen JSON-WebSocket-Zugang für Browser bereit. Darüber lassen sich unter anderem Anrufe steuern, Zustände und Anrufereignisse empfangen, Kontakte abfragen, Firefox-Tabs über ein externes Plugin verwalten sowie Gesprächsaufnahmen starten, stoppen und als WAV übertragen. Das aktuelle WebSocket-Protokoll ist Version 11.
SwyxIt!, das Firefox-Plugin SwyxFFPlugin, SwyxWeb und SwyxTrans sind nicht Teil dieses Repositories. Die ausführliche fachliche und Protokolldokumentation steht in README.md.
Technologie und zentrale Konfiguration
- C# mit .NET 10; die Anwendung zielt auf
net10.0-windows, die Checks aufnet10.0. - Windows Forms mit
OutputType=WinExe; sichtbar sind primärNotifyIcon, Kontextmenü, Sprechblasen und ein kleiner Einstellungsdialog. - Einzige externe NuGet-Abhängigkeit in beiden Projekten:
Swyx.Client.ClmgrAPI14.21.0. Diese Version ist auf die verwendete SwyxIt!-Version 14.21 abgestimmt und sollte bei einem Client-Update mitgeführt werden. - Das SDK stellt die COM-Interop-Typen aus
IpPbx.CLMgrLibbereit. Es wählt die Interop-Assembly anhand von$(Platform)aus;AnyCPUist daher nicht zulässig. - Die Anwendung ist standardmäßig auf
x64, Runtime Identifierwin-x64und framework-abhängige Ausführung eingestellt.x86ist inPlatformsaufgeführt, der vorhandene Runtime Identifier bleibt jedochwin-x64; x64 ist der dokumentierte und geprüfte Weg. - Nullable Reference Types und Implicit Usings sind aktiviert. Globalisierung ist invariant.
- Es gibt keine
.sln, keinglobal.json, kein.editorconfig, kein Paket-Lockfile und keine CI-Konfiguration im Repository.
Wichtige Dateien:
| Pfad | Aufgabe |
|---|---|
src/SwyxTray/SwyxTray.csproj |
Zielframework, WinForms, Plattform, Runtime und SDK-Paket |
src/SwyxTray/app.manifest |
asInvoker, Windows-10/11-Kompatibilität, PerMonitorV2-DPI und Long-Path-Unterstützung |
tests/SwyxTray.RecordingChecks/SwyxTray.RecordingChecks.csproj |
Eigenständiges Konsolenprogramm; bindet ausgewählte Produktionsdateien direkt ein |
.vscode/tasks.json |
Debug-/Release-Builds und Start in der Desktop-Sitzung |
.vscode/launch.json |
Direkter Start beziehungsweise Anhängen des Debuggers |
.vscode/start-desktop.ps1 |
Temporäre geplante Aufgabe für den Start in der interaktiven Sitzung |
README.md |
Verhalten, WebSocket-Protokoll, Sicherheit, Betrieb und bisherige Prüfungen gegen den realen Client |
Codeorganisation und Laufzeitarchitektur
Anwendung und Oberfläche
src/SwyxTray/Program.cs: STA-Einstiegspunkt, sitzungslokale Einzelinstanz-Sperre (Local\SwyxTray.SingleInstance),--verbose/-v, COM-Sicherheitsinitialisierung und globale Fehlerprotokollierung.src/SwyxTray/TrayApplicationContext.cs: Anwendungsrumpf, Tray-Menü, Timer, Verbindungsaufbau, Einstellungen und Weitergabe von Zustandsänderungen an Oberfläche und WebSocket-Server.src/SwyxTray/TrayIcons.cs: Erzeugt und cached die farbigen Symbole zur Laufzeit; es gibt keine ICO-Ressourcen.src/SwyxTray/SettingsDialog.cs: Einstellung der Verzögerung, mit der Firefox bei eingehenden Rufen in den Vordergrund geholt wird.src/SwyxTray/FirefoxWindow.cs: Native Windows-Aufrufe zum Aktivieren des Firefox-Fensters nach der Tab-Aktivierung durch das Plugin.src/SwyxTray/NativeMethods.cs: COM-Sicherheit und Freigabe nativer Icon-Handles.src/SwyxTray/Log.cs: Schlankes, threadübergreifend gesperrtes Dateiprotokoll ohne Logging-Framework.
Swyx-/COM-Schicht (src/SwyxTray/Swyx/)
SwyxClient.cskapselt CLMgr-Verbindung, Ereignissenke, Snapshots, Telefoniebefehle, Kontaktsuche, Telefonbuchcache, Aufnahmebefehle und COM-Lebenszyklus.SwyxModels.csenthältSwyxSnapshot,SwyxLineInfo,SwyxContactund den aggregierten ZustandSwyxOverallState.LineStateExtensions.csklassifiziert die SDK-Leitungszustände und liefert deutsche Anzeigetexte.PubCLMgrMessages.csbildet die benötigten Werte aus dem C-HeaderCLMgrPubTypes.hnach.CallRecordingTracker.csverfolgt erfolgreich an das SDK übergebene Aufnahmewünsche pro Leitung undcallId. Dies ist ausdrücklich kein Nachweis, dass tatsächlich eine Audiodatei geschrieben wurde.
Zentrale Invarianten:
- Alle COM-Zugriffe bleiben im WinForms-STA-Thread. Netzwerk- und Hintergrund-Threads dürfen CLMgr nicht direkt aufrufen.
UiDispatcherundCommandExecutorschleusen Befehle mit einer Zeitgrenze von zehn Sekunden in den UI-Thread. NativeMethods.InitializeComSecurity()muss vor dem ersten COM-Aufruf erfolgen, damit CLMgr Ereignisse in den Prozess zurückrufen kann.- Zustandsänderungen sind primär ereignisgesteuert. Bei jeder CLMgr-Meldung wird der vollständige Zustand neu gelesen. Ein Sicherheitszyklus läuft alle fünf Sekunden, bei fehlendem Ereignisrückkanal jede Sekunde.
- COM-Objekte werden an den vorhandenen Stellen mit
Marshal.FinalReleaseComObjectfreigegeben. Neue COM-Pfade müssen Ereignisabmeldung, Fehlerfälle und Freigabe ebenso behandeln. - CLMgr-Leitungen sind intern 0-basiert, im Tray-Menü und WebSocket-Protokoll dagegen 1-basiert.
- Telefonbücher kommen als reine IDispatch-Objekte an und werden über
IEnumerablesowiedynamic/Late Binding gelesen, nicht über die typisierten Telefonbuch-Interop-Schnittstellen. - Der Adresscache wird beim Verbinden und anschließend alle 60 Minuten neu gelesen. Bei Lesefehlern bleibt der letzte erfolgreiche Stand erhalten.
WebSocket-Schicht (src/SwyxTray/Web/)
LocalWebSocketServer.cs: MehrereTcpListener, Verbindungsverwaltung und Broadcasts. Der Hauptport bedient Browser; ein zweiter Port ist ausschließlich für das Firefox-Plugin vorgesehen.WebSocketHandshake.cs: Manueller HTTP-Upgrade nach RFC 6455, einschließlich Token-, Origin- und Host-Prüfung. WebSocket-Frames, Maskierung, Ping/Pong und Close übernimmtSystem.Net.WebSockets.WebSocketSession.cs: Empfang und serialisiertes Senden einer Verbindung. Eingehende Textnachrichten sind auf 64 KiB begrenzt; Sendevorgänge sind durch eine Sperre serialisiert. Normale Kommandos werden pro Sitzung nacheinander verarbeitet.CommandExecutor.cs: Übersetzt Kommandos inSwyxClient- beziehungsweise Plugin-Aufrufe und validiert unter anderem Rufnummern und absolute HTTP(S)-URLs.UiDispatcher.cs: Übergabe von Arbeit an den UI-SynchronizationContext.PluginTabChannel.cs: Anfrage-/Antwortzuordnung fürlist,open,closeundactivatemit fünf Sekunden Antwortzeit. Der Plugin-Port erlaubt nurtabresultundping.Protocol.cs: JSON-DTOs, Snapshot-Abbildung und Ableitung der Anrufereignisse. JSON verwendet Web-Defaults (camelCase) und lässt Nullwerte weg.WebSocketConfig.cs: Protokollversion, Konfiguration, Token-Erzeugung und Sicherheitsschalter.
Bei Broadcasts wird zuerst der Snapshot und unmittelbar danach die daraus abgeleiteten call-Ereignisse gesendet. Ein Verbindungs- oder Serverausfall allein gilt nicht als nachgewiesenes Gesprächsende.
Aufnahme-Warteschlange (src/SwyxTray/Recording/RecordingOutbox.cs)
RecordingOutbox verbindet SDK-Aufnahmeabschnitte mit neu erzeugten WAV-Dateien und hält deren Zustand persistent. Wichtige Regeln:
- Manifeste liegen als UUID-benannte JSON-Dateien in der Warteschlange; Schreibvorgänge erfolgen über temporäre Dateien und anschließendes Ersetzen.
- Nur eindeutig zuordenbare, neue, geschlossene WAVs mit stabiler Länge und gültiger RIFF/WAVE-Struktur werden übernommen. Die maximale Größe beträgt 500 MB.
- Die ursprüngliche SwyxIt!-Aufnahme wird weder verändert noch gelöscht; übertragen wird eine temporäre Kopie.
- Ein Aufnahmestopp allein gibt die Datei nicht frei. Halten oder ein COM-/Serverausfall gelten nicht als Gesprächsende. Erst ein gesunder Snapshot mit beendeter oder ersetzter Verbindung bestätigt das Ende.
- Offene Aufnahmen überstehen einen Neustart. Erst nach einer dauerhaft gespeicherten
recording.ack-Bestätigung mit 32-stelliger hexadezimalerjobIdwird die temporäre Kopie entfernt. recording.downloadüberträgt binäre Blöcke mitSWYX-Magic, Kommando-ID und Offset (jeweils Int32 Little Endian) sowie höchstens 64 KiB Audiodaten. Die normale Empfangsschleife bleibt für Steuerkommandos verfügbar.
Bauen, Starten und Prüfen
Alle Befehle werden aus dem Repository-Stamm ausgeführt. Vorausgesetzt werden Windows, ein .NET-10-SDK und für den realen Betrieb ein laufendes SwyxIt! in derselben Benutzersitzung.
Build
dotnet build src/SwyxTray/SwyxTray.csproj -p:Platform=x64
dotnet build src/SwyxTray/SwyxTray.csproj -c Release -p:Platform=x64
Die Debug-Ausgabe liegt unter:
src/SwyxTray/bin/x64/Debug/net10.0-windows/win-x64/SwyxTray.exe
bin/ und obj/ sind ignorierte Build-Artefakte und gehören nicht in Änderungen.
Automatische Checks
dotnet run --project tests/SwyxTray.RecordingChecks -p:Platform=x64
Dies ist kein MSTest-/xUnit-/NUnit-Projekt und nicht mit dotnet test gleichzusetzen. tests/SwyxTray.RecordingChecks/Program.cs ist ein ausführbares Prüfprogramm mit eigenen Check-/Reject-Hilfen; jede nicht bestandene Bedingung wirft eine Ausnahme. Erfolg wird auf der Konsole mit der Anzahl bestandener Checks gemeldet.
Die Checks umfassen derzeit:
- Aufnahmezustand, idempotentes Starten, Fehlerfälle und veraltete
callId-Werte; - Snapshot-/Ereignisfelder und Unterscheidung von Gesprächsende, Halten und Verbindungsverlust;
- WAV-Erkennung, Dateisperren, unvollständige Dateien, Mehrdeutigkeit, Gesprächsende ohne lokalen Auflegebefehl und persistente Bestätigungen;
- echten Loopback-WebSocket-Binärtransport einschließlich Übertragungsabbruch, Wiederholung und parallelem
ping.
Das Testprojekt kompiliert Produktionsdateien direkt ein und ersetzt nur COM-Executor und Plugin-Kanal durch TransportStubs.cs. Temporäre WAVs und Warteschlangen liegen unter dem System-Tempverzeichnis und werden aufgeräumt. Es finden keine COM-Aufrufe oder Telefonate statt.
Für einen manuellen Browser-Fixture-Server existiert zusätzlich:
dotnet run --project tests/SwyxTray.RecordingChecks -p:Platform=x64 -- --serve
Dieser Modus läuft dauerhaft auf Loopback-Port 17658 und stellt eine synthetische WAV bereit; er ist kein Bestandteil des normalen Prüflaufs.
Anwendung starten
dotnet build src/SwyxTray/SwyxTray.csproj -c Release -p:Platform=x64
.\src\SwyxTray\bin\x64\Release\net10.0-windows\win-x64\SwyxTray.exe --verbose
--verbose protokolliert zusätzlich jede CLMgr-Meldung. Eine zweite Instanz in derselben Sitzung beendet sich wegen der Einzelinstanz-Sperre.
In VS Code ist „SwyxTray debuggen (Desktop-Sitzung)" der geeignete Weg, besonders bei Remote-VS-Code. Die Aufgabe beendet zunächst eine laufende Instanz, baut Debug und startet die Anwendung über eine geplante Aufgabe im interaktiven Desktop. Der direkte Start kann bei einer Remote-Sitzung in Sitzung 0 landen; dort läuft der Prozess ohne sichtbares Tray-Symbol.
Verifizierungsumfang und manuelle Restprüfungen
Bei Erstellung dieses Leitfadens wurden der Debug-Build und der normale Prüflauf ausgeführt: Build erfolgreich mit 0 Warnungen und 0 Fehlern, 204 recording/protocol checks passed (no COM calls).
Die automatischen Checks ersetzen keine Prüfung mit realem SwyxIt!. COM-Aktivierung, Ereignisrückkanal, echte Zustandswechsel, Telefoniebefehle, tatsächliche Swyx-Aufnahmen, Tray-Darstellung und Firefox-Fokusverhalten erfordern eine interaktive Windows-Sitzung und gegebenenfalls kontrollierte Testanrufe. Keine realen Anrufe allein zur Verifikation auslösen.
Entwicklungskonventionen
- Sprache der Dokumentation, UI und Protokollmeldungen ist Deutsch. Einige neuere Aufnahme- und Testdateien verwenden englische Kommentare; an der jeweiligen Datei orientieren.
- Vier Leerzeichen Einrückung, dateibezogene Namespaces, geschweifte Klammern und die vorhandenen Benennungsmuster beibehalten. Es gibt keinen separaten Formatter- oder Analyzer-Workflow.
- Asynchroner Netzwerkcode verwendet durchgehend
ConfigureAwait(false)außerhalb des UI-Kontexts. Fortsetzungen werden bei bedarfsgesteuertenTaskCompletionSource-Instanzen asynchron ausgeführt. - Ressourcen und Abonnements werden explizit aufgeräumt:
IDisposable, CancellationTokens, Sendesperren, COM-Freigaben, Tray-Icons und Event-Handler beachten. - Die vorhandene schlanke Architektur beibehalten und keine neuen Frameworks oder Abhängigkeiten ohne konkreten Bedarf einführen.
- Protokolländerungen müssen
WebSocketConfig.ProtocolVersion, DTOs, Verhalten, Checks undREADME.mdkonsistent aktualisieren. Bestehende Felder wieid,line,callId,recordingId, Snapshot/Ereignis-Reihenfolge und das Binärformat sind öffentliche Schnittstellen. - Neue Aufnahme- oder Transportregeln gehören in das ausführbare Check-Programm. Für gemeinsam genutzte Produktionsdateien muss gegebenenfalls auch die
<Compile Include=...>-Liste des Check-Projekts angepasst werden. - Die im README verlinkte Datei
examples/browser-client.htmlist im aktuellen Repository nicht vorhanden. Nicht annehmen, dass ein Browserbeispiel oder das externe Plugin lokal verfügbar ist.
Laufzeitdaten und Konfiguration
Die von SwyxTray selbst angelegten Laufzeitdaten liegen unter %LOCALAPPDATA%\SwyxTray (die ursprünglichen SwyxIt!-Aufnahmen bleiben dagegen im konfigurierten Swyx-Aufnahmeordner):
swyxtray.log: Dateiprotokoll, Rotation bei 1 MiB mit einer Sicherungsdateiswyxtray.log.1;websocket.json: WebSocket-Konfiguration;recordings/: persistente JSON-Manifeste und temporäre WAV-Kopien der Aufnahme-Warteschlange.
Beim ersten Start wird websocket.json mit zufälligem Token erzeugt. Die aktuellen Standardwerte sind:
- Hauptport
17654, Plugin-Port17655(0deaktiviert den Plugin-Port); Enabled=true,MaxConnections=8;AllowedOrigins=[]undAllowRemoteAccess=false;IncomingCallTabTitle="SwyxWeb"undIncomingCallFocusDelayMs=1000.
Eine defekte Konfiguration führt bewusst dazu, dass der Server nicht startet, statt still ein neues Token zu erzeugen. Änderungen an der JSON-Datei erfordern grundsätzlich einen Neustart. Die Fokusverzögerung kann über den Einstellungsdialog geändert werden; dieser speichert sofort in dieselbe geladene Konfiguration. Beim Speichern gehen manuell eingefügte JSON-Kommentare verloren.
Sicherheitsaspekte
Kritischer Ist-Zustand: WebSocketConfig.SecurityDisabled ist in src/SwyxTray/Web/WebSocketConfig.cs auf true gesetzt – in jedem Build. Token-, Origin- und Host-Prüfung sind derzeit abgeschaltet. Wer den Haupt- oder Plugin-Port erreicht, kann die angebotenen Aktionen ausführen und Daten mitlesen. Dieser Zustand ist laut README ausschließlich für die Erprobung vorgesehen und darf nicht ausgeliefert werden.
Standardmäßig bindet die Anwendung nur an 127.0.0.1 und ::1. Mit AllowRemoteAccess=true bindet sie an alle IPv4-/IPv6-Schnittstellen. In Kombination mit den abgeschalteten Prüfungen kann dann grundsätzlich jeder erreichbare Netzwerkteilnehmer telefonieren und mitlesen. Vor einer Auslieferung müssen die Prüfungen wieder aktiviert und der gewünschte Bindingscope bewusst festgelegt werden.
Bei aktivierter Sicherheit bestehen die vorgesehenen Schranken aus:
- zufälligem Token, zeitkonstant verglichen, per Query-Parameter oder
token.…-Unterprotokoll; - Origin-Allowlist, da die Same-Origin-Policy WebSockets nicht schützt;
- Host-Prüfung gegen DNS-Rebinding bei Loopback-Betrieb;
- Loopback-Bindung beziehungsweise bewusster Fernzugriff mit Firewall-Konfiguration.
Bei Fernzugriff entfällt die Host-Prüfung auch im abgesicherten Modus. Es gibt nur ws://, kein wss://; HTTPS-Seiten können entfernte unverschlüsselte WebSockets wegen Mixed-Content-Regeln gegebenenfalls nicht verwenden.
Die Eingabevalidierung nicht abschwächen: Rufnummern werden bereinigt und begrenzt, Browser-URLs müssen absolutes HTTP(S) verwenden, JSON- und Headergrößen sind begrenzt, WAVs werden strukturell geprüft und Downloads/Bestätigungen benötigen den passenden persistenten Zustand. Token und Aufnahmeinhalte nicht unnötig in neue Protokollmeldungen oder Commits aufnehmen.
Die Anwendung läuft mit asInvoker und benötigt im Normalbetrieb keine Administratorrechte. CoInitializeSecurity verwendet derzeit die dokumentierte, wenig restriktive COM-Ereignissenken-Konfiguration; Änderungen daran müssen gegen den realen CLMgr-Rückruf geprüft werden. Anwendungssteuerungsrichtlinien wie WDAC/Smart App Control müssen außerdem den von Swyx benötigten COM-Proxy CLMgrPs64.dll zulassen.
Deployment und Betrieb
Der Standardbuild ist framework-abhängig und benötigt die .NET-10-Desktop-Laufzeit. Das README dokumentiert alternativ eine eigenständige Veröffentlichung:
dotnet publish src/SwyxTray/SwyxTray.csproj -c Release -r win-x64 --self-contained true -p:Platform=x64
Es gibt weder Installer noch automatisierte Release-/Deployment-Pipeline im Repository. Für Autostart wird eine Verknüpfung zur EXE in shell:startup beziehungsweise %APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup abgelegt.
SwyxTray darf nicht einfach als Windows-Dienst betrieben werden: Dienste laufen in Sitzung 0, während SwyxIt!/CLMgr zur interaktiven Benutzersitzung gehören. Anwendung und Client müssen in derselben Sitzung und mit kompatiblem Integritätslevel laufen. Bei Netzwerkzugriff kann zusätzlich eine eingehende Windows-Firewall-Regel für den Haupt- und gegebenenfalls Plugin-Port erforderlich sein.