aimux
aimux is an AI-agent-friendly tmux wrapper.
Use aimux when you need a long-lived shell session — a REPL, a build/server you want to interact with across multiple commands, or anything running on a remote SSH host. Use bash directly for one-shot commands. aimux gives you session primitives, file-transfer helpers, and remote-host helpers; this skill explains how to combine them.
Install
pip install aimux # or `uv pip install aimux` if using uv venv
Usage
aimux is tool that enables tmux-like capability cross ssh.
(.venv) /home/.../...$ aimux -h
Usage: aimux [OPTIONS] COMMAND [ARGS]...
aimux — AI-agent-friendly tmux wrapper.
─ Options
--help -h Show this message and exit.
─ Device Operation Commands
remote Remote-host management (reads/appends ~/.ssh/config).
bridge Bridge broker management.
reverse Reverse-connect this machine to an aimux bridge broker.
mcp Run a stdio MCP server exposing a bridge remote to external agents.
─ Session Operation Commands
ls List all aimux sessions.
new Create a new session.
attach Attach to a session. ⚠️ Human use only — agents should use send-keys + capture.
send-keys Send keys to a session. Behaves exactly like 'tmux send-keys': supports -F/-H/-K/-l/-M/-R/-X/-N <count>. Pass 'Enter' as a key to submit a line.
kill Destroy a single session. Does not accept wildcards or batch.
capture Capture pane output (last N lines).
wait-last-command-complete Block until the pane's foreground process is back at a shell. Local sessions only.
─ File Operation Commands
send-files Shortcut over the file API: upload files or directories (mkdir + write).
get-files Shortcut over the file API: download files or directories (stat + read).
sync-dir Shortcut over the file API: differentially sync a directory (list + read/write + archive).
─ File Read/Edit Commands
read Read a remote file: metadata header (path, encoding, size, sha256), then content.
apply-patch Apply a patch to create, edit, or delete files (stdin, or --file).
─ Advanced Port Operation Commands
local-http-port-reroute Listen on a local port and tunnel it to another bridge client's allowed HTTP port.
port Forward local HTTP traffic through an aimux bridge broker.
Every command also carries worked examples: run aimux <command> -h.
Example
- adding and naming a remote host in
aimux.
First, usually you will get a ssh address, for example,
HostName localhost
Port 8824
User root
To begin, you will need to check whether this address is already inside aimux's storage:
$ aimux remote ls
HOST USER HOSTNAME PORT STATUS RTT
github.com git github.com 22 auth-required 2261ms
local-8824 root localhost 8824 reachable 709ms
If not in storage, you will have to add that server using:
aimux remote add --host localhost --port 8824 --user root --name local-8824 --timeout 2s
aimux remote test local-8824 --timeout 2s
- Creating a session and run command in it.
aimux new --remote local-8824 --name testsession
aimux send-keys "local-8824/testsession" -- 'ls -la' Enter
aimux capture "local-8824/testsession" --lines 200
- Creating a bridge session.
Bridge remotes are reverse-connected clients. Start a broker on the server,
connect a client back to it, then create sessions through the same --remote
API. Bridge remotes require an explicit profile:
# server
aimux bridge deploy --port=8765
# client
aimux reverse connect http://SERVER:8765 --identifier=winbox --token=<token>
# server
aimux remote ls
aimux new --remote=winbox --profile=cmd --name=cmd-1
aimux send-keys winbox/cmd-1 -- "echo hello" Enter
aimux capture winbox/cmd-1 --lines=50
Windows bridge profiles include cmd, powershell, pwsh, and mingw64
when available. macOS exposes mac-pty and mac-terminal; Linux exposes
posix-pty.
On a Windows client you can also opt into computer use. Pass --enable-gui
and the client downloads a small accessibility helper (UIA-based, single
binary, sha256-verified) to ~/.aimux/helpers/, starts it, and reports the
GUI capability with its registration. The helper never needs an OS permission
prompt; it must simply run inside a logged-in desktop session (not over SSH).
A failed download or probe never blocks the connection — the remote just
registers with GUI off:
aimux reverse connect http://SERVER:8765 --identifier=winbox --token=<token> --enable-gui
aimux remote ls shows a GUI column (on/off) so the capability stays
visible.
- Forward a browser-facing HTTP service through the bridge server.
aimux port forward https://SERVER/aimux_bridge \
--local-port=8080 \
--remote-host=127.0.0.1 \
--remote-port=3000 \
--token=<token>
Open http://127.0.0.1:8080. Ordinary HTTP requests, streaming SSE responses,
and WebSocket upgrades are forwarded to http://127.0.0.1:3000 from the
bridge server's network namespace. Use --remote-scheme=https for an HTTPS
target.
- Reach a port on another bridge client's loopback.
port forward (above) dials the target from the broker's network. When the
service instead lives on a reverse-connected machine behind NAT, the target
client has to do the dialling — and it will only do so for ports it explicitly
allowed.
# client A: declare which local ports other clients may reach (default: none)
aimux reverse connect http://SERVER:8765 \
--identifier=a-box --token=<token> \
--allow-http-ports=8000,8001
# client B: listen locally on 9000 and tunnel to a-box's 127.0.0.1:8000
aimux local-http-port-reroute \
--local-port=9000 \
--remote-client-id=a-box \
--remote-client-http-port=8000 \
--bridge-url=http://SERVER:8765 --token=<token>
Now http://127.0.0.1:9000 on B reaches http://127.0.0.1:8000 on A. The
tunnel moves raw bytes, so ordinary HTTP, streaming SSE responses, and
WebSocket upgrades all work — and, as with port forward, nothing is parsed or
rewritten in between.
- Expose a remote to an MCP agent — optionally with computer use.
# classic tools only (remote_exec_command, remote_write_stdin, ...)
aimux mcp serve --remote=winbox
# classic + computer-use tools (remote_gui_diagnostics, remote_gui_list_windows,
# remote_gui_look, remote_gui_act, remote_gui_read_text, remote_gui_wait_for,
# remote_gui_focus_window)
aimux mcp serve --remote=winbox --toolset=classic-and-gui
GUI tools drive the remote's accessibility tree: remote_gui_look returns an
element outline (with refs) plus a screenshot, and remote_gui_act acts on a
ref semantically through UIA patterns. The default policy ax_only never
injects real mouse/keyboard input; raw input requires explicitly passing
policy=foreground. Serving --toolset=classic-and-gui against a remote
without GUI fails fast with the reason.
The command is symmetric: run it on A too (naming B) to get the reverse
direction. Ports are checked twice — the broker refuses a port the target
never allowed, and the target re-checks the allowlist itself before dialling —
so a client that never passes --allow-http-ports cannot be connected to at
all. Targets are always dialled on 127.0.0.1; there is no --remote-host, so
a tunnel can never become a jump host onto the rest of A's network.
Because the traffic is tunnelled rather than proxied, B's listener sees the
bytes exactly as A's service emitted them: absolute URLs, cookies, and Host
headers all refer to A's loopback address, not to B's.
The listener binds 127.0.0.1 by default. Pass --share-to-non-localhost to
bind 0.0.0.0 and let other machines reach the tunnel through B as well —
note that the listener carries no authentication of its own, so anyone who can
reach that port reaches the tunnelled service. --local-host <addr> picks a
specific interface instead.
- Upload and download files.
File transfer for bridge remotes
aimux send-files and aimux get-files work the same way for bridge remotes
as for SSH hosts — the CLI auto-detects the remote type and dispatches
accordingly. Bridge transfers use a base64-encoded file.* message family
over the existing SSE+POST channel, with no sftp dependency on the client.
aimux send-files winbox 'C:\Users\dev\proj' ./src --gitignore
aimux get-files winbox ./logs 'C:\Users\dev\proj\logs'
Differences vs the SSH path:
- Single-file size limit defaults to 16 MiB (configurable via
AIMUX_BRIDGE_MAX_FILE_SIZEif you want to override it in code). - Files are written with mode 0o644; existing timestamps/permissions are not preserved.
- Symlinks are not followed on the remote side — they show up as
symlinkentries in directory listings and are skipped during recursive download. ..segments in remote paths are rejected to prevent traversal.
# aimux send-files REMOTE REMOTE_DIR LOCAL_PATH... [--gitignore]
# aimux get-files REMOTE LOCAL_DIR REMOTE_PATH...
aimux send-files local-8824 /tmp/upload /home/testdir/tests
aimux get-files local-8824 /home/testdir/tests /tmp/upload
Both commands report every path on both sides as an absolute path — once before the transfer starts, once after it finishes:
uploading 2 file(s) to local-8824:/tmp/upload
/home/testdir/tests/a.txt -> local-8824:/tmp/upload/tests/a.txt
/home/testdir/tests/sub/b.txt -> local-8824:/tmp/upload/tests/sub/b.txt
uploaded 2 file(s) to local-8824:/tmp/upload
/home/testdir/tests/a.txt -> local-8824:/tmp/upload/tests/a.txt
/home/testdir/tests/sub/b.txt -> local-8824:/tmp/upload/tests/sub/b.txt
Use --gitignore on upload when ignored files should be skipped:
aimux send-files local-8824 /tmp/upload /home/testdir/tests --gitignore
- Clean up.
aimux kill "local-8824/testsession"
Metadata
Release files for aimux 0.3.68
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| aimux-0.3.68.tar.gz | 232.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| aimux-0.3.68-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 368.5 kB
Release files / aimux-0.3.68.tar.gz
| Download URL | aimux-0.3.68.tar.gz |
|---|---|
| Size | 232.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e279d0ae67404922afca25de1abd469f25a1a0017acd937015dd7025761b6443
|
|
BLAKE2b-256 checksum How to use checksums |
d1e225c99bd6ee28e6e8854a688bd04db688fc9b8f1ae4a0288966d7f5c22ace
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.3
|
Release files / aimux-0.3.68-py3-none-any.whl
| Download URL | aimux-0.3.68-py3-none-any.whl |
|---|---|
| Size | 136.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a8dee5ac40574e774ec3e63f04ca2b4f389754eff24c8fbc7c0d543f68b8bfee
|
|
BLAKE2b-256 checksum How to use checksums |
ec981042630eae61d19e1212d24a92aafd36fefad74c96e45fa3f115fff92a90
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.3
|