- Weist auf die Komplexität und die Grenzen der bestehenden Terminal-Struktur hin und skizziert ein Next-Generation-Terminal-Konzept, das Eingabe, Ausgabe und Prozesssteuerung neu integriert
- Nimmt Jupyter Notebook als Modell und lotet die Möglichkeit aus, interaktive Interfaces mit Bild-Rendering, erneutem Ausführen von Befehlen, editierbarer Ausgabe und integriertem Editor umzusetzen
- Erläutert anhand der Beispiele Warp und iTerm2 konkret die tiefe Integration zwischen Shell und Terminal (shell integration), das Management lang laufender Prozesse sowie Funktionen zur Sitzungs-Trennung und -Wiederherstellung
- Entwirft auf Basis von Dataflow-Tracking und Persistenz erweiterte Funktionen wie Undo/Redo für Befehle, automatische erneute Ausführung und kollaborative Terminals
- Stellt eine schrittweise Aufbaustrategie vor, die sich von transaktionaler CLI → persistenter Sitzung → strukturiertem RPC → Jupyter-artigem Frontend weiterentwickelt
Grundstruktur des Terminals
- Ein Terminal besteht aus vier Komponenten: Terminal-Emulator, virtuelles Terminal (PTY), Shell und Prozessgruppen
- Der Terminal-Emulator ist das Programm, das eine Rasterstruktur auf dem Bildschirm rendert
- Das PTY ist ein interner Kernel-Zustand, der Eingaben an die Prozessgruppe weiterleitet und Signale umwandelt
- Die Shell liest und parst Eingaben und fungiert als Event-Loop, der Prozesse erzeugt
- Prozesse interagieren über Ein- und Ausgabe mit den genannten Komponenten
- Eingaben sind nicht bloß Text, sondern enthalten auch Signale, und die Ausgabe besteht aus ANSI-Escape-Sequenzen, die Formatierung ausdrücken
Entwurf eines besseren Terminals
- Bestehende Terminals haben viele funktionale Einschränkungen und bieten daher zu wenig Erweiterbarkeit und Interaktivität
- Jupyter Notebook bietet Funktionen, die in einem traditionellen VT100-Emulator nicht möglich sind
- Rendering hochauflösender Bilder
- Ein Button zum „Erneut ausführen von Anfang an“, der frühere Ausgabe ersetzt statt sie anzuhängen
- „Views“, in denen Quellcode und Ausgabe direkt an Ort und Stelle umgeschrieben werden können, etwa wenn Markdown als Quelltext oder als gerendertes HTML angezeigt wird
- Ein integrierter Editor mit Syntax-Highlighting, Tabs, Panels und Mausunterstützung
- Das Konzept eines Jupyter-Notebooks, das die Shell als Kernel verwendet, stößt jedoch auf mehrere Probleme
- Die Shell erhält Befehle auf einmal, sodass Tab-Completion, Syntax-Highlighting und Auto-Suggestions nicht funktionieren
- Probleme bei der Behandlung lang laufender Prozesse: Jupyter führt standardmäßig aus, bis eine Zelle abgeschlossen ist; Abbrechen ist möglich, aber Pausieren, Fortsetzen, Interaktion und das Anzeigen laufender Prozesse sind nicht möglich
- Ein „Zelle erneut ausführen“-Button kann den Zustand des Computers problematisch beeinflussen, insbesondere wenn Befehle wie
rm -rf enthalten sind
- Undo/Redo funktioniert nicht
Wie könnte das funktionieren?
-
Shell-Integration (Shell Integration)
- Das Terminal Warp baut eine native Integration zwischen Terminal und Shell auf
- Das Terminal versteht Beginn und Ende jedes Befehls, dessen Ausgabe und die Benutzereingabe
- Die Umsetzung erfolgt über Standardfunktionen, konkret benutzerdefinierte DCS
- Auch iTerm2 kann auf ähnliche Weise OSC-133-Escape-Codes verwenden
- Navigation zwischen Befehlen mit nur einem Shortcut
- Benachrichtigung nach Abschluss eines Befehls
- Anzeige des aktuellen Befehls als „Overlay“, wenn die Ausgabe aus dem Bildschirmbereich herausläuft
-
Management lang laufender Prozesse
- Interaktion:
- Um mit lang laufenden Prozessen zu interagieren, ist bidirektionale Kommunikation erforderlich
- Beispiele für TUI:
top, gdb, vim
- Jupyter ist stark bei der Gestaltung interaktiver Ausgaben, die sich ändern und aktualisieren lassen
- Eine denkbare Terminal-Funktion wäre eine stets verfügbare „freie Eingabezelle“
- Der interaktive Prozess läuft oben im Fenster, unten befindet sich eine Eingabezelle
- Pausieren:
- Das „Anhalten“ eines Prozesses wird als Job Control bezeichnet
- Von modernen Terminals wäre zu erwarten, dass sie angehaltene und Hintergrundprozesse mit einer dauerhaften visuellen Anzeige darstellen
- Ähnlich wie IntelliJ unten in der Taskleiste „Indexing...“ anzeigt
- Trennen der Verbindung: Für Sitzungs-Trennung und -Wiederherstellung gibt es drei Ansätze
- Tmux / Zellij / Screen: Zwischen Terminal-Emulator und Programm wird ein zusätzlicher Terminal-Emulator eingeschoben. Der Server besitzt das PTY und rendert die Ausgabe, während der Client sie im eigentlichen Terminal-Emulator anzeigt. Clients können getrennt und wieder verbunden werden, mehrere Clients können gleichzeitig verbunden sein. iTerm arbeitet mit einem eigenen Client, der den tmux-Client umgeht und direkt mit dem Server kommuniziert
- Mosh: Ein SSH-Ersatz. Unterstützt das erneute Verbinden mit einer Terminal-Sitzung nach Netzunterbrechungen. Auf dem Server läuft eine Zustandsmaschine, die inkrementelle Unterschiede des Viewports an den Client wiedergibt. Multiplexing und Scrollback sollen vom Terminal-Emulator übernommen werden. Da der Client tatsächlich netzwerkseitig lokal läuft, ist lokales Zeilen-Editing sofort reaktionsfähig
- alden/shpool/dtach/abduco/diss: Behandeln im Client/Server-Modell nur das Trennen und Fortsetzen von Sitzungen, enthalten aber weder Networking noch Scrollback und auch keinen eigenen Terminal-Emulator. Gegenüber tmux und mosh ist der Grad der Entkopplung höher
-
Erneutes Ausführen und Zurücksetzen
- Die Lösung ist Dataflow-Tracking
- pluto.jl setzt das heute um, indem es sich an den Julia-Compiler anschließt
- Zellen, die von früheren Zellen abhängen, werden in Echtzeit aktualisiert
- Wenn sich Abhängigkeiten nicht ändern, werden Zellen nicht aktualisiert
- Im Grunde ein tabellenkalkulationsähnliches Jupyter, das Code nur dann erneut ausführt, wenn es nötig ist
- Verallgemeinerung durch orthogonale Persistenz
- Prozesse werden in Sandboxes ausgeführt und sämtliches I/O wird nachverfolgt, wodurch „zu seltsame“ Dinge verhindert werden, solange keine Kommunikation mit anderen Prozessen außerhalb der Sandbox stattfindet
- Prozesse lassen sich als reine Funktion ihrer Eingaben behandeln; diese Eingaben sind das „gesamte Dateisystem, alle Umgebungsvariablen und alle Prozesseigenschaften“
Abgeleitete Funktionen
- Ein Jupyter-Frontend ist nötig:
- Runbooks (eigentlich bereits allein mit Jupyter- und PTY-Primitiven umsetzbar)
- Terminal-Anpassung mit normalem CSS ohne seltsame benutzerdefinierte Sprachen oder ANSI-Farbcodes
- Befehlssuche nach Ausgabe/Zeitstempel: Man kann derzeit die gesamte Ausgabe der aktuellen Sitzung oder den gesamten Verlauf der Befehlseingaben durchsuchen, aber es gibt keine intelligenten Filter, und Ausgaben bleiben nicht sitzungsübergreifend erhalten
- Shell-Integration ist nötig:
- Zeitstempel und Laufzeit jedes Befehls
- Lokales Zeilen-Editing auch über Netzwerkgrenzen hinweg
- IntelliSense für Shell-Befehle ohne Drücken der Tab-Taste, mit ins Terminal integrierter Darstellung
- Sandbox-Tracking ist nötig:
- Alle Möglichkeiten des Sandbox-Trackings: kollaborative Terminals, Abfragen von durch Befehle geänderten Dateien, zur Laufzeit bearbeitbare Asciinema-Aufzeichnungen, Build-System-Tracking
- Erweiterung der intelligenten Suche, damit auch nach dem Plattenzustand zum Zeitpunkt der Befehlsausführung gesucht werden kann
- Ausweitung von Undo/Redo auf ein git-ähnliches Verzweigungsmodell (emacs undo-tree unterstützt das bereits), mit mehreren „Views“ auf den Prozessbaum
- Mit dem Undo-Tree-Modell und Sandboxing lässt sich LLMs Zugriff auf ein Projekt geben und mehrere davon parallel ausführen, ohne dass sie gegenseitig Zustände überschreiben; ihre Arbeit kann geprüft, bearbeitet und als später nutzbares Runbook gespeichert werden
- Ein Terminal, das in Produktionsumgebungen nur bestehenden Zustand inspiziert, ohne den Maschinenzustand zu beeinflussen
Schrittweise Aufbaustrategie
-
Schritt 1: Transaktionale Semantik (transactional semantics)
- Beim Redesign des Terminals vom Terminal-Emulator aus zu beginnen, ist der falsche Ansatz
- Nutzer hängen an ihrem Emulator und an dessen Konfiguration, Erscheinungsbild und Keybindings
- Die Wechselkosten für den Emulator sind hoch
- Der richtige Weg ist, auf der CLI-Ebene zu beginnen
- CLI-Programme lassen sich leicht installieren und ausführen und verursachen sehr geringe Wechselkosten
- Sie können einmalig genutzt werden, ohne den gesamten Workflow zu ändern
- Entwicklung einer CLI, die transaktionale Semantik für das Terminal implementiert
- Eine Schnittstelle wie
transaction [start|rollback|commit]
- Alles, was nach
start ausgeführt wird, kann rückgängig gemacht werden
- Schon damit ließe sich ein ganzes Business aufbauen
-
Schritt 2: Persistente Sitzungen (Persistent Sessions)
- Nach der transaktionalen Semantik wird Persistenz von tmux und mosh getrennt
- Um PTY-Persistenz zu erhalten, ist ein Client/Server-Modell nötig
- Der Kernel geht davon aus, dass beide Seiten des PTY stets verbunden sind
- Mit einem Befehl wie alden oder einer ähnlichen Bibliothek lässt sich das einfach umsetzen, ohne Programme zu beeinflussen, die im Terminal-Emulator oder in der PTY-Sitzung laufen
- Für Scrollback speichert der Server Ein- und Ausgabe unbegrenzt und spielt sie beim erneuten Verbinden des Clients wieder ab
- Der Terminal-Emulator bietet nativen Scrollback, den er wie jede andere Ausgabe behandelt
- Wiedergabe und Fortsetzung ab einem beliebigen Startpunkt sind möglich
- Dafür ist das Parsen von ANSI-Escape-Codes nötig, aber mit ausreichend Aufwand machbar
- Für Netzwerkwiederaufnahme wie bei mosh wird Eternal TCP verwendet, das sich zur Effizienzsteigerung auf QUIC aufsetzen ließe
- PTY-Persistenz und Persistenz der Netzwerkverbindung werden getrennt
- Eternal TCP ist eine reine Optimierung: Es könnte auf einem Bash-Skript aufbauen, das in einer Schleife
ssh host eternal-pty attach ausführt
- An diesem Punkt können wie bei tmux mehrere Clients an eine einzelne Terminal-Sitzung angebunden werden, während die Fensterverwaltung weiterhin vom Terminal-Emulator übernommen wird
- Wenn integriertes Fenster-Management gewünscht ist, kann der Terminal-Emulator wie iTerm das tmux-
-CC-Protokoll verwenden
- Alle Teile dieser Phase lassen sich unabhängig von der transaktionalen Semantik parallel umsetzen, reichen aber noch nicht aus, um ein Business darauf aufzubauen
-
Schritt 3: Strukturiertes RPC
- Baut auf dem Client/Server-Modell auf
- Wenn ein Server zwischen Terminal-Emulator und Client eingreift, lassen sich Funktionen wie I/O-Tagging mit Metadaten umsetzen
- Allen Daten können Zeitstempel hinzugefügt werden
- Eingabe und Ausgabe lassen sich unterscheiden
- xterm.js arbeitet auf ähnliche Weise
- In Kombination mit Shell-Integration lässt sich auf der Datenebene zwischen Shell-Prompt und Programmausgabe unterscheiden
- Damit entsteht ein strukturiertes Log der Terminal-Sitzung
- Logs können wie bei asciinema als Aufzeichnung wiedergegeben werden
- Shell-Prompts lassen sich transformieren, ohne alle Befehle erneut auszuführen
- Import in Jupyter Notebook oder Atuin Desktop ist möglich
- Befehle lassen sich speichern und später erneut als Skript ausführen
- Das Terminal wird zu Daten
-
Schritt 4: Jupyter-ähnliches Frontend
- Hier wird zum ersten Mal der Terminal-Emulator angetastet und bewusst erst ganz am Schluss
- Denn hier sind die Wechselkosten am höchsten
- Nutzt alle aufgebauten Funktionen, um eine gute UI bereitzustellen
- Die
transaction-CLI wird nicht mehr benötigt, sofern keine verschachtelten Transaktionen gewünscht sind
- Die gesamte Terminal-Sitzung beginnt standardmäßig als Transaktion
- Da alle Bausteine kombiniert sind, werden alle oben erwähnten abgeleiteten Funktionen bereitgestellt
Fazit
- Diese Architektur ist kühn und ambitioniert; für die vollständige Umsetzung werden bis zu 10 Jahre veranschlagt
- Sie soll mit Geduld Schritt für Schritt vorangetrieben werden
- Es bleibt zu hoffen, dass dieser Text jemanden inspiriert, selbst mit dem Aufbau zu beginnen
Noch keine Kommentare.