first commit

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-20 11:05:58 +02:00
co-authored by Claude Opus 5
commit 99757361ef
15 changed files with 3020 additions and 0 deletions
+260
View File
@@ -0,0 +1,260 @@
# Swyx Tab Bridge — Firefox-Add-on
Verwaltet Firefox-Tabs über die WebSocket-Verbindung zu SwyxTray: Tabs auslesen, öffnen
und schließen.
## Rollenverteilung
Ein Browser-Add-on kann **keinen** WebSocket-Server betreiben — es kann nur Client sein.
Das Add-on **verbindet sich** daher zu SwyxTray (Default `ws://127.0.0.1:17655`) und
beantwortet die dort eintreffenden Aufträge.
```
[ SwyxTray, Port 17655 ] <--- verbindet sich --- [ Firefox-Add-on: WS-Client ]
| {"type":"tab","id":1,"action":"list"} ->
<- {"cmd":"tabresult","id":1,"ok":true,"tabs":[…]}
```
Zuständig ist das Add-on **ausschließlich für Tab-Nachrichten** (`"type":"tab"`).
Alles andere auf der Verbindung — Telefonie-Kommandos, Events, Nachrichten an andere
Komponenten — wird ignoriert: keine Antwort, kein Fehler.
## Protokoll
Alle Nachrichten sind JSON-Textframes. Die `id` der Anfrage wird unverändert in die
Antwort gespiegelt; die Antwort trägt immer `"cmd":"tabresult"`.
### 1. Offene Tabs auslesen (`list`)
```json
{"type":"tab","id":1,"action":"list"}
{"cmd":"tabresult","id":1,"ok":true,
"tabs":[{"id":42,"title":"Kundenakte Muster GmbH","url":"https://crm.example.local/kunden/4711","active":false},
{"id":43,"title":"Posteingang","url":"https://mail.example.local/","active":true}]}
```
Optionale Felder zum Einschränken: `currentWindowOnly` (bool), `windowId` (number),
`url` / `title` (Match-Pattern, z.B. `"*://*.example.local/*"`).
### 2. Tab mit mitgegebener URL öffnen (`open`)
```json
{"type":"tab","id":2,"action":"open","url":"https://crm.example.local/kunden/4711"}
{"cmd":"tabresult","id":2,"ok":true,"tabId":44}
```
Optionale Felder:
| Feld | Typ | Default | Beschreibung |
|-------------|---------|---------|-----------------------------------------------------------------|
| `active` | boolean | `true` | `false` = im Hintergrund öffnen |
| `reuse` | boolean | `false` | ist die URL schon offen, wird der Tab aktiviert statt neu geöffnet (Antwort enthält dann `"reused":true`) |
| `newWindow` | boolean | `false` | in neuem Fenster öffnen |
| `windowId` | number | | Zielfenster |
| `index` | number | | Position in der Tableiste |
| `pinned` | boolean | `false` | angeheftet öffnen |
Erlaubt sind `http`, `https`, `ftp`, `file`; `javascript:`, `data:` u.ä. werden abgewiesen.
### 3. Tab löschen (`close`)
```json
{"type":"tab","id":3,"action":"close","tabId":42}
{"cmd":"tabresult","id":3,"ok":true}
```
`tabId` darf auch ein Array sein (`[42,43]`); die Antwort enthält dann `"closed":[42,43]`.
### Anmeldung (`hello`)
Direkt nach dem Verbindungsaufbau meldet sich das Add-on einmal an — immer, nicht
nur mit Token:
```json
{"cmd":"hello","client":"swyx-tab-bridge","version":"1.0.0",
"actions":["list","open","close","activate","focus"]}
```
Ist unter *Einstellungen* ein Token gesetzt, kommt es als Feld `token` mit. Eine
Antwort wird nicht erwartet. SwyxTray kann die Gegenstelle erst dadurch
identifizieren und den Verbindungsaufbau protokollieren; ohne `hello` ist auf der
Leitung nicht unterscheidbar, wer sich verbunden hat.
### Fehler
```json
{"cmd":"tabresult","id":5,"ok":false,"error":"Feld 'url' fehlt."}
```
`error` ist ein zusätzliches Klartextfeld zur Diagnose — maßgeblich ist `"ok":false`.
Fehlerfälle: fehlendes/ungültiges `url` bzw. `tabId`, unerlaubtes URL-Protokoll,
unbekannte `action`, nicht existierender Tab.
SwyxTray wertet einen Auftrag nach 5 s ohne Antwort als gescheitert. Das Add-on bricht
deshalb jede Aktion nach 4 s ab (`ACTION_TIMEOUT_MS` in [`background.js`](background.js))
und antwortet dann mit `"ok":false` — es bleibt also nie stumm.
### Zusätzliche Aktionen
Über die drei SwyxTray-Aktionen hinaus versteht das Add-on:
```json
{"type":"tab","id":4,"action":"activate","tabId":42}
{"cmd":"tabresult","id":4,"ok":true,"tabId":42}
{"type":"tab","id":5,"action":"focus","url":"https://crm.example.local/kunden/4711"}
{"cmd":"tabresult","id":5,"ok":true,"focused":true,"opened":false,"tabId":42}
```
- `activate` holt einen bekannten Tab per `tabId` in den Vordergrund (inkl. Fenster).
- `focus` sucht die passende Seite und aktiviert sie; ist sie nicht offen, wird sie
geöffnet (`focused:false, opened:true`). Optional: `match` (`prefix` | `exact` |
`origin`, Default `prefix`), `open:false` (nicht öffnen), `activate:false`,
oder Suche per `title` statt `url`. Fragment (`#…`) und abschließende Slashes werden
beim Vergleich ignoriert.
### Ignorierte Nachrichten
| Nachricht | Verhalten |
|----------------------------------------------------|--------------------|
| alles ohne `"type":"tab"` (z.B. `{"cmd":"call",…}`) | wird ignoriert |
| ungültiges JSON | wird ignoriert |
## Installation (temporär, zum Entwickeln)
1. `about:debugging#/runtime/this-firefox` öffnen
2. **„Temporäres Add-on laden…”** → `manifest.json` in diesem Ordner auswählen
3. Server-Adresse ggf. unter *Einstellungen* im Popup anpassen (Default `ws://127.0.0.1:17655`)
Temporär geladene Add-ons verschwinden beim Firefox-Neustart. Für den Dauerbetrieb muss
das Add-on signiert werden (`web-ext sign`, AMO-Account) oder es wird Firefox ESR /
Developer Edition mit `xpinstall.signatures.required = false` verwendet.
Paket bauen:
```bash
npx web-ext lint
npx web-ext build # erzeugt web-ext-artifacts/*.zip -> signieren bzw. in .xpi umbenennen
```
## Signierung und Datenerhebung
Seit dem 3. November 2025 müssen neue Erweiterungen auf AMO Mozillas eingebautes
Einwilligungssystem bedienen. Im Manifest steht deshalb:
```json
"data_collection_permissions": { "required": ["browsingActivity"] }
```
Mozilla definiert Übermittlung als Daten, die *außerhalb des Add-ons oder des
lokalen Browsers* verarbeitet werden. Das Add-on gibt Tab-Titel und URLs an
SwyxTray weiter — also aus dem Browser heraus, wenn auch nur an eine lokale
Anwendung auf demselben Rechner und nicht ins Netz. `"none"` wäre eine
Untertreibung, `browsingActivity` beschreibt genau das (besuchte Seiten, URLs).
`strict_min_version` bleibt bewusst bei `115.0`, obwohl der Schlüssel erst ab
Firefox 140 ausgewertet wird. Ältere Versionen ignorieren ihn folgenlos; ein
Anheben auf `140.0` würde ESR 115 und ESR 128 ausschließen. `web-ext lint` meldet
dafür zwei Warnungen — die blockieren die Signierung nicht.
## Installation auf Windows (per SSH)
`deploy/deploy-windows.sh` zählt die Version hoch, baut das `.xpi`, überträgt es per
`scp` und führt `deploy/Install-SwyxTabBridge.ps1` auf dem Zielrechner aus:
```bash
./deploy/deploy-windows.sh # Testrechner, ohne Parameter
./deploy/deploy-windows.sh --keep-firefox # laufenden Firefox nicht beenden
./deploy/deploy-windows.sh --uninstall # rückgängig machen
```
Der nackte Aufruf erledigt alles: Version hochzählen, bei AMO signieren, übertragen,
Policy schreiben und den laufenden Firefox beenden (nur den aus dem Zielverzeichnis —
eine zweite Installation daneben bleibt offen). Ziel ist die Release-Installation, die
ausschließlich signierte Add-ons annimmt; jeder Lauf wird deshalb signiert. Ziel-Rechner,
Firefox-Verzeichnis sowie Signatur- und Neustart-Verhalten sind als Konstanten oben im
Skript voreingestellt.
Standardmäßig wird eine Enterprise-Policy (`force_installed`) geschrieben, sodass das
Add-on beim nächsten Firefox-Start ohne Rückfrage für alle Profile installiert wird.
Details, Signierung und Optionen: [deploy/README.md](deploy/README.md).
## Tests
Selbsttest der Kommandoverarbeitung ohne Firefox — `background.js` läuft mit gestubbten
`browser`-/`WebSocket`-APIs:
```bash
node test-server/test-background.mjs
```
Testserver, der SwyxTray simuliert (ohne Abhängigkeiten, nur Node):
```bash
node test-server/server.js # lauscht auf ws://127.0.0.1:17655
node test-server/server.js --log /pfad/x.log # anderes Protokoll
node test-server/server.js --no-log # nur Konsole
```
Der Server protokolliert Verbindungsaufbau, Anmeldung und Trennung mit Zeitstempel
auf der Konsole **und** in `test-server/swyx-tray.log`:
```
[2026-08-17 16:16:09.809] Verbindung geöffnet von 127.0.0.1.
[2026-08-17 16:16:09.809] Angemeldet: swyx-tab-bridge v1.0.12 (Aktionen: list, open, close, activate, focus; ohne Token)
[2026-08-17 16:16:12.314] Verbindung getrennt: swyx-tab-bridge v1.0.12 (Dauer 3 s).
```
Meldet sich eine Gegenstelle binnen drei Sekunden nicht mit `hello` an, wird das
als „unbekannte Gegenstelle" vermerkt. Das ist die Vorlage dafür, was SwyxTray
mitschreiben sollte.
| Eingabe | gesendete Nachricht |
|------------------------------------|--------------------------------------------------------|
| `list` / `list current` | `{"type":"tab","action":"list"}` |
| `open <url> [background]` | `{"type":"tab","action":"open","url":"…"}` |
| `close <tabId>` | `{"type":"tab","action":"close","tabId":42}` |
| `activate <tabId>` | `{"type":"tab","action":"activate","tabId":42}` |
| `focus <url> [titel…]` | `{"type":"tab","action":"focus","url":"…"}` |
| `raw {…}` | beliebiges JSON (ohne `type:"tab"` bleibt es unbeantwortet) |
| `quit` | beenden |
## Einstellungen
Die Einstellungsseite zeigt oben, ob das Add-on mit der Tray-App verbunden ist —
Zustand, Serveradresse, Zeitpunkt des Verbindungsaufbaus und der letzte Fehler,
laufend aktualisiert. Dieselbe Anzeige gibt es kompakt im Popup.
Über das Symbol in der Symbolleiste → *Einstellungen*:
- **WebSocket-Server** — `ws://…` oder `wss://…` (Default `ws://127.0.0.1:17655`)
- **Token** — optionales Shared Secret. Ist es gesetzt, geht es in der
`hello`-Nachricht mit.
- **Automatisch verbinden** — Auto-Reconnect mit exponentiellem Backoff (1 s → max. 30 s)
Das Symbol zeigt den Verbindungszustand: kein Badge = verbunden, `…` = Verbindungsaufbau,
`!` = getrennt.
## Dateien
| Datei | Zweck |
|-----------------------------------|---------------------------------------------------|
| `manifest.json` | Add-on-Manifest (MV2, persistenter Hintergrund) |
| `background.js` | WebSocket-Client, Reconnect, Aktionen |
| `popup.html/js` | Statusanzeige, Verbinden/Trennen |
| `options.html/js` | Server-URL, Token, Auto-Connect |
| `test-server/server.js` | Testserver (simuliert SwyxTray) |
| `test-server/test-background.mjs` | Selbsttest der Kommandoverarbeitung |
| `deploy/deploy-windows.sh` | Version hochzaehlen, bauen, per SSH ausrollen |
| `deploy/Install-SwyxTabBridge.ps1`| Installation auf dem Windows-Rechner |
## Hinweise
- **Manifest V2** ist bewusst gewählt: Nur mit persistentem Hintergrundskript bleibt die
WebSocket-Verbindung dauerhaft offen. Unter MV3 (nicht-persistente Event-Page) würde die
Verbindung nach Leerlauf abgebaut. Firefox unterstützt MV2 weiterhin.
- `ws://127.0.0.1` ist erlaubt (das Add-on läuft im `moz-extension:`-Kontext, keine
Mixed-Content-Blockade). Für Verbindungen über das Netzwerk `wss://` verwenden.
- SwyxTray sollte nur auf `127.0.0.1` lauschen — sonst kann jeder im Netz Tabs öffnen,
schließen und die Tab-Liste mitlesen.
- Die Permission `tabs` ist nötig, um `title` und `url` der Tabs zu lesen.