リモート tmux ミラーリング
リモート tmux は、cmux から SSH でサーバーに接続し、その tmux セッションにコントロールモード(tmux -CC)でアタッチして、cmux のインターフェースで表示します。セッション、ウィンドウ、ペインはそれぞれワークスペース、タブ、分割として表示され、ネイティブのスクロールバックとコピーが使えます。一方、すべての変更は接続先の実際の tmux サーバーに適用されます。この機能はベータ機能のトグルで有効にするもので、ローカルのターミナルには影響しません。
始める前に
リモートマシンには tmux 3.2 以降が必要で、SSH で接続できる必要があります。~/.ssh/config のホストエイリアスも使えます。標準の tmux にはコントロールモードが最初から含まれています。アタッチする前に cmux はサーバーの tmux バージョンを確認し、3.2 より古い場合は明確なエラーで停止します。接続には SSH ControlMaster を使い、デタッチ後も tmux サーバーは動き続けます。
有効にする
リモート tmux はベータ機能で、初期状態ではオフです。設定 → ベータ機能で有効にするまで、ローカルのターミナルには影響しません。
ホストへの接続
ターミナルで cmux ssh-tmux <宛先>(~/.ssh/config のエイリアスまたは user@host)を実行します。cmux はそのホストの tmux セッションを現在のウィンドウのサイドバーにミラーリングします。各セッションはワークスペースに、各ウィンドウはタブに、複数ペインのウィンドウはタブ内の分割になります。
# user@host, or any Host alias from ~/.ssh/config
cmux ssh-tmux build@devbox.internal
# non-default port and key
cmux ssh-tmux staging --port 2200 --identity ~/.ssh/staging_ed25519
# put the mirror in its own cmux window
cmux ssh-tmux build@devbox.internal --new-window非対話で認証されるホスト(ssh-agent や ~/.ssh/config の鍵)はプロンプトなしで接続されます。対話的な認証(パスワード、ホストキーの確認、多要素認証)が必要なホストでは、cmux がそのターミナル内で ssh を実行するため、その場で認証でき、その後 cmux が現在のウィンドウにミラーリングします。--port、--identity、--no-focus を指定できます。
--new-window を付けると、ミラーしたセッションをコマンドを実行したウィンドウではなく、別の cmux ウィンドウに配置します。
"Permission denied" の対処
cmux は接続先をそのまま ssh に渡します。そのため Host エイリアスは、ログインに必要な情報をすべて単独で提供する必要があります。通常は User(指定しないと ssh はローカルのアカウント名でログインします)と IdentityFile です。例:
Host staging
HostName 198.51.100.24
Port 2200
User deploy
IdentityFile ~/.ssh/staging_ed25519cmux ssh-tmux dev@my-ssh-alias のように、接続先にユーザー名を含めることもできます。簡単なテストとして、同じ接続先で普通に ssh を実行してみてください。それでログインできない場合、cmux ssh-tmux も同じように失敗します。
tmux の要素と cmux での対応
2 つのツールは、要素の入れ子の順序が逆です。tmux のウィンドウはペインに分割されますが、cmux のペインはタブの列を持ち、タブごとにターミナルが 1 つあります。cmux は tmux の各階層を cmux のコンテナに変換することで、この違いを調整します:
| tmux での要素 | cmux での表示 |
|---|---|
session | サイドバー上の独立したワークスペース |
window | そのワークスペース内の 1 つのタブ |
pane | そのタブ内のネイティブな分割の片側 |
その結果、cmux の 1 つのタブに複数のターミナルが入ることがあります。複数のペインを持つ tmux ウィンドウは、そのタブ内の実際の cmux 分割として配置され、リモートの各ペインには通常の cmux ターミナルの装飾が付きます。変更は逆方向にも反映されます。これらのペインを分割したり閉じたりすると split-window で tmux ウィンドウに適用され、タブを並べ替えると swap-window で tmux ウィンドウも並べ替えられます。
サポートされる動作
- 分割: ミラーしたタブでペインを追加・削除すると、それぞれ tmux の split-window 呼び出しになり、cmux は tmux が返すレイアウトから再描画します。スクリプトからの分割(cmux new-split、surface.split)はサーフェス ID なしで accepted を返し、ペインは tmux が変更を反映した後に表示されます。この経路では tmux に起動コマンド、作業ディレクトリ、区切り線の位置、左または上への配置を渡せないため、cmux はそうした要求をすぐに拒否し、リモートセッションには手を加えません。
- グリッドサイズ: cmux はリモートクライアントを実際に描画しているグリッドに合わせてリサイズする(refresh-client -C)ため、全画面プログラムは tmux のデフォルトの 80×24 ではなく実際の大きさを受け取ります。
- マウス入力: リモートのプログラムがマウスモードを有効にすると、クリック、ホイールスクロール、ドラッグはそのプログラムに届きます。代わりに cmux でテキストを選択してコピーしたい場合は、ローカルのマウス対応プログラムと同じく、Shift を押しながらドラッグしてください。
- ペーストとドラッグ&ドロップ: ペーストしたテキストや、ドロップした画像・ファイルは tmux paste-buffer -p で送られるため、リモートのプログラム(たとえばコーディングエージェント)には本物のブラケットペーストとして届きます。画像は Mac 上のパスではなく、[Image #N] として表示されます。
- 正しい Unicode 表示: tmux がマルチバイト文字を 2 つの出力チャンクにまたがって分割した場合、cmux が断片を結合し直すため、罫線や全角グリフが正しく表示されます。
- 現在のフォルダー: cmux はリモートの各ペインの作業ディレクトリを追跡し、タブに表示します。
- タブの順序: ミラーしたタブをドラッグして並べ替えると、swap-window で tmux のウィンドウも並べ替えられます。
仕組み
ミラーの実体は、ただの ssh … tmux -CC attach プロセスです。cmux はコントロールモードのクライアントを自前で実装しています。各応答ブロック(%begin … %end)をそれを発生させたコマンドと対応付け、%output 通知をそのペインのターミナルサーフェスへ順次流し込み、キー入力とマウスイベントを tmux send-keys として送り返します。レイアウトの正はサーバー側にあるため、cmux は tmux が報告する内容からサーフェスのサイズを決め、ローカルでのリフローは行いません。
ソケット API
この機能はソケットコマンドからも操作できます(ベータ機能フラグでゲートされます)。host は SSH の宛先または ~/.ssh/config のエイリアス、session は tmux のセッション名です。mirror は通常の window/workspace/surface/pane ルーティングも受け取ります。
remote.tmux.* のソケットコマンド(下記)でより細かく制御できます。remote.tmux.mirror がミラーリングのエントリポイントで、ウィンドウや呼び出し元のルーティングを受け取り、必要に応じてミラーワークスペースを選択できます。
| メソッド名 | パラメーター | 動作 |
|---|---|---|
remote.tmux.mirror | host, port?, identity_file?, activate? | ホスト上のすべてのセッションを解決されたウィンドウにミラーリングします(各セッションがワークスペース、ウィンドウがタブになります)。ホストが必要とする場合は、対話的認証のために実行すべき ssh コマンドを返します。 |
remote.tmux.sessions | host, port?, identity_file? | ホスト上に存在する tmux セッションを返します。 |
remote.tmux.attach | host, session, create? | 1 つのセッションに対してコントロールクライアントを開始します。create を指定すると、存在しないセッションを先に作成します。 |
remote.tmux.state | host, session | コントロールクライアントが現在認識している状態の診断用スナップショットです。 |
remote.tmux.detach | host, session | コントロールクライアントを停止し、リモートセッションはそのまま残します。 |
{"method": "remote.tmux.sessions", "params": {"host": "staging"}}SSH オプションの注入を防ぐため、ダッシュで始まるホストまたは ID ファイルは、要求を受け取った時点で拒否されます。
既知の制限
- SSH が一時的に切れてもミラーは終了しません。cmux はペインを固定し、上限付きの指数バックオフで再接続し、接続が戻るとペインの内容を再び読み込みます。ミラーが完全に終了するのは、リモートセッションの終了、ミラーのウィンドウを閉じたとき、cmux を終了したときの 3 つの場合です。再起動後は cmux ssh-tmux でもう一度開始してください。ミラーはセッション復元の対象外です。
- リフローは tmux に任せているため、サイズが変わってもメイン画面のスクロールバックは折り返し直されません。古い行は、プログラムが再描画するまで以前の幅のまま残ることがあります。全画面プログラムはリサイズ時に再描画するため影響を受けません。
- paste-buffer -p によるブラケットペーストは、ドロップしたファイルや画像のパスのような 1 行の内容にしか対応していません。複数行にわたるテキストは通常のキー入力として送られるため、リモートのプログラムはそれを 1 回のペーストとして扱いません。
- 作業ディレクトリの追跡はコントロールモードのサブスクリプションに依存しています。そのため cmux は 3.2 より古い tmux に対してはミラーを開きません。