Mimari
Tarayıcı, Node.js sunucusu, tmux ve Claude CLI'nin nasıl bir araya geldiği.
purplemux birbirine dikilmiş üç katmandır: bir tarayıcı ön ucu, :8022'de bir Node.js sunucusu ve host üzerindeki tmux + Claude CLI. Aralarındaki her şey ya bir ikili WebSocket ya da küçük bir HTTP POST'tur.
Üç katman
Browser Node.js server (:8022) Host
───────── ──────────────────────── ──────────────
xterm.js ◀──ws /api/terminal──▶ terminal-server.ts ──node-pty──▶ tmux (purple socket)
Timeline ◀──ws /api/timeline──▶ timeline-server.ts │
Status ◀──ws /api/status────▶ status-server.ts └─▶ shell ─▶ claude
Sync ◀──ws /api/sync──────▶ sync-server.ts
status-manager.ts ◀──POST /api/status/hook── status-hook.sh
rate-limits-watcher.ts ◀──POST /api/status/statusline── statusline.sh
JSONL watcher ──reads── ~/.claude/projects/**/*.jsonl
Her WebSocket'in tek bir amacı vardır; multiplex yapmazlar. Kimlik doğrulama, WS yükseltmesi sırasında doğrulanan bir NextAuth JWT çerezidir.
Tarayıcı
Ön uç bir Next.js (Pages Router) uygulamasıdır. Sunucuyla konuşan parçalar:
| Bileşen | Kütüphane | Amaç |
|---|---|---|
| Terminal paneli | xterm.js |
/api/terminal'den gelen baytları çizer. Tuş vuruşlarını, yeniden boyutlandırma olaylarını, başlık değişikliklerini (onTitleChange) yayar. |
| Oturum zaman tüneli | React + useTimeline |
/api/timeline'dan Claude turlarını çizer. cliState türetimi yok — hepsi sunucu tarafı. |
| Durum göstergeleri | Zustand useTabStore |
/api/status mesajlarıyla sürülen sekme rozetleri, kenar çubuğu noktaları, bildirim sayıları. |
| Çoklu cihaz senkronizasyonu | useSyncClient |
Başka bir cihazda yapılan çalışma alanı / düzen düzenlemelerini /api/sync üzerinden izler. |
Sekme başlıkları ve ön plan süreci, xterm.js'in onTitleChange olayından gelir — tmux her iki saniyede bir #{pane_current_command}|#{pane_current_path} yaymak üzere yapılandırılmıştır (src/config/tmux.conf) ve lib/tab-title.ts onu ayrıştırır.
Node.js sunucu
server.ts, Next.js artı aynı portta dört ws WebSocketServer örneği barındıran özel bir HTTP sunucusudur.
WebSocket uç noktaları
| Yol | Handler | Yön | Kullanım |
|---|---|---|---|
/api/terminal |
terminal-server.ts |
çift yönlü, ikili | Bir tmux oturumuna bağlı node-pty üzerinden terminal G/Ç |
/api/timeline |
timeline-server.ts |
sunucu → istemci | JSONL'den ayrıştırılan Claude oturum girişlerini akıtır |
/api/status |
status-server.ts |
çift yönlü, JSON | Sunucudan status:sync / status:update / status:hook-event, istemciden status:tab-dismissed / status:ack-notification / status:request-sync |
/api/sync |
sync-server.ts |
çift yönlü, JSON | Çapraz cihaz çalışma alanı durumu |
Ayrıca ilk-çalıştırma kurulumcusu için /api/install (auth gerekmez).
Terminal ikili protokolü
/api/terminal, src/lib/terminal-protocol.ts'de tanımlanan küçük bir ikili protokol kullanır:
| Kod | Ad | Yön | Yük |
|---|---|---|---|
0x00 |
MSG_STDIN |
istemci → sunucu | Tuş baytları |
0x01 |
MSG_STDOUT |
sunucu → istemci | Terminal çıktısı |
0x02 |
MSG_RESIZE |
istemci → sunucu | cols: u16, rows: u16 |
0x03 |
MSG_HEARTBEAT |
her ikisi | 30 sn aralık, 90 sn timeout |
0x04 |
MSG_KILL_SESSION |
istemci → sunucu | Alttaki tmux oturumunu sonlandır |
0x05 |
MSG_WEB_STDIN |
istemci → sunucu | Web girdi çubuğu metni (kopya modundan çıkıştan sonra teslim edilir) |
Geri basınç: WS bufferedAmount > 1 MB olduğunda pty.pause, 256 KB altında devam et. Sunucu başına en fazla 32 eşzamanlı bağlantı, sonrasında en eskisi düşürülür.
Status manager
src/lib/status-manager.ts, cliState için tek doğruluk kaynağıdır. Hook olayları /api/status/hook (token-doğrulanmış POST) üzerinden akar, sıralanır (sekme başına eventSeq) ve deriveStateFromEvent tarafından idle / busy / needs-input / ready-for-review / unknown durumlarına indirgenir. JSONL izleyici, tek bir sentetik interrupt olayı dışında yalnızca metadata günceller.
Tam durum makinesi için Oturum durumu (STATUS.md) sayfasına bakın.
tmux katmanı
purplemux, kendi yapılandırmasını src/config/tmux.conf'ta kullanan özel bir sokette (-L purple) yalıtılmış bir tmux çalıştırır. Sizin ~/.tmux.conf'unuz hiç okunmaz.
Oturumlar pt-{workspaceId}-{paneId}-{tabId} olarak adlandırılır. Tarayıcıdaki bir terminal paneli, node-pty ile bağlanan bir tmux oturumuna eşlenir.
tmux socket: purple
├── pt-ws-MMKl07-pa-1-tb-1 ← tarayıcı sekmesi 1
├── pt-ws-MMKl07-pa-1-tb-2 ← tarayıcı sekmesi 2
└── pt-ws-MMKl07-pa-2-tb-1 ← bölünmüş panel, sekme 1
prefix devre dışıdır, durum çubuğu kapalıdır (kromu xterm.js çizer), set-titles açıktır ve mouse on tekerleği copy-mode'a koyar. tmux, oturumların kapalı bir tarayıcı, Wi-Fi düşmesi veya sunucu yeniden başlatmasından sağ çıkmasının sebebidir.
Tam tmux kurulumu, komut sarmalayıcı ve süreç tespit detayları için tmux & süreç tespiti (TMUX.md) sayfasına bakın.
Claude CLI entegrasyonu
purplemux Claude'u fork etmez veya sarmalamaz — claude ikili dosyası kurduğunuz her ne ise odur. İki şey eklenir:
- Hook ayarları — Başlangıçta
ensureHookSettings()~/.purplemux/hooks.json,status-hook.shvestatusline.sh'i yazar. Her Claude sekmesi--settings ~/.purplemux/hooks.jsonile başlar, böyleceSessionStart,UserPromptSubmit,Notification,Stop,PreCompact,PostCompacthepsi sunucuya geri POST eder. - JSONL okumaları —
~/.claude/projects/**/*.jsonl, canlı konuşma görünümü içintimeline-server.tstarafından ayrıştırılır ve~/.claude/sessions/'taki PID dosyaları aracılığıyla çalışan bir Claude sürecini tespit etmek içinsession-detection.tstarafından izlenir.
Hook betikleri ~/.purplemux/port ve ~/.purplemux/cli-token'ı okur ve x-pmux-token ile POST eder. Sunucu kapalıysa sessizce başarısız olurlar, böylece Claude çalışırken purplemux'ı kapatmak hiçbir şeyi çökertmez.
Başlangıç sırası
server.ts:start() bunları sırayla çalıştırır:
acquireLock(port)—~/.purplemux/pmux.locküzerinden tek-örnek koruyucuinitConfigStore()+initShellPath()(kullanıcının login shellPATH'ini çözer)initAuthCredentials()— scrypt-hashlı parolayı ve HMAC gizliliğini env'e yüklerscanSessions()+applyConfig()— ölü tmux oturumlarını temizler,tmux.conf'u uygularinitWorkspaceStore()—workspaces.jsonve çalışma alanı başınalayout.json'ı yüklerautoResumeOnStartup()— kayıtlı dizinlerde shell'leri yeniden başlatır, Claude resume'u denergetStatusManager().init()— metadata yoklamasını başlatırapp.prepare()(Next.js dev) veyarequire('.next/standalone/server.js')(prod)bindPlan.host:portüzerindelistenWithFallback()(erişim politikasına göre0.0.0.0veya127.0.0.1)ensureHookSettings(result.port)— gerçek port ile hook betiklerini yazar veya yenilergetCliToken()—~/.purplemux/cli-token'ı okur veya üretirwriteAllClaudePromptFiles()— her çalışma alanınınclaude-prompt.md'sini yeniler
Port çözümü ile adım 10 arasındaki pencere, hook betiklerinin her başlangıçta yeniden üretilmesinin nedenidir: canlı portun pişmiş olması gerekir.
Custom server ile Next.js modül grafiği
Daha fazla okuma
docs/TMUX.md— tmux yapılandırma, komut sarmalayıcı, süreç ağacı yürüme, terminal ikili protokol.docs/STATUS.md— Claude CLI durum makinesi, hook akışı, sentetik kesinti olayı, JSONL izleyici.docs/DATA-DIR.md— purplemux'ın yazdığı her dosya.
Sıradaki adımlar
- Veri dizini — yukarıdaki mimarinin dokunduğu her dosya.
- CLI referansı — sunucuyla tarayıcı dışından konuşmak.
- Sorun giderme — buradaki bir şey kötü davrandığında teşhis.