handmux 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,17 +4,9 @@
4
4
 
5
5
  [![npm](https://img.shields.io/npm/v/handmux?color=cb3837&logo=npm)](https://www.npmjs.com/package/handmux) [![CI](https://github.com/handmux/handmux/actions/workflows/test.yml/badge.svg)](https://github.com/handmux/handmux/actions/workflows/test.yml) [![license: AGPL-3.0](https://img.shields.io/badge/license-AGPL--3.0-blue)](LICENSE) [![node](https://img.shields.io/badge/node-%E2%89%A518-339933?logo=node.js&logoColor=white)](https://nodejs.org)
6
6
 
7
- > **Drive your agent, ditch the desk.** *Keep your creativity in hand* one command on your
8
- > computer, scan a QR, and your live terminal (agent and all) goes wherever you do.
7
+ > **One phone, a whole mobile vibe-coding cockpit.** Built on tmux — one command on your computer, scan a QR, and your live session, Claude Code, Codex, git, previews and docs are all in your hand, creativity ready wherever you are.
9
8
 
10
- handmux puts the *same* live **tmux** session that's on your computer into your phone's browser —
11
- **real panes, not a read-only mirror**. Spin up a brand-new session right from your phone, or pick up
12
- one already running at your desk — then keep steering it from the couch, the train, a queue at the
13
- coffee shop. The agent never stops; you just change screens.
14
- **Nothing to install on the phone — open a link and you're in**, or add it to your home screen as a
15
- PWA that runs full-screen, basically like a native app. It works with any shell or TUI, and
16
- goes deepest with **Claude Code**: it pushes you the moment a pane needs a decision, and you approve
17
- with your thumb.
9
+ handmux is more than a terminal on your phone. It puts the *same* live **tmux** session running on your computer into your phone's browser (real panes, not a read-only mirror), then builds a whole **mobile vibe-coding cockpit** around it: **Claude Code / Codex** push you the moment a pane needs a decision approve with your thumb, or fire off a new instruction by voice; browse a full-screen colored **git** diff; **preview** a running site in one tap; hear a **doc** read aloud line by line; move files both ways. Nothing to install on the phone — open a link and you're in; "Add to Home Screen" and it runs full-screen as a **PWA**, basically a native app. Curl up on the couch or squeeze onto the train — the vibe coding never stops, your creativity stays in hand.
18
10
 
19
11
  <p align="center">
20
12
  <img src="assets/handmux-run.gif" alt="handmux: say what you need, Claude Code writes it, then tap the filename to preview the result" width="280">
@@ -24,296 +16,62 @@ with your thumb.
24
16
  <em>Real phone browser, real panes — say what you need and Claude Code writes it, then tap a filename to preview (left); a push pings you when needed, and you review the git repo &amp; each agent's usage (right).</em>
25
17
  </p>
26
18
 
27
- ## Why handmux
28
-
29
- - **🚀 One command to go live.** `handmux start`, scan the QR — done. No account, no App Store, no
30
- native app to sideload. Just a link your phone opens in any browser.
31
- - **🧶 Your real session, in your pocket.** Not a fresh shell and not a screenshot — the *exact* tmux
32
- panes from your computer, agent still running. Desk → phone → desk, same session the whole way.
33
- - **🤖 Made for vibe coding with agents.** Claude Code and Codex are first-class: a push the instant
34
- one needs you, an inbox of which pane is *working / waiting on you / done*, per-agent usage bars, and
35
- plan & permission approvals you tap through. aider and any other shell/TUI work too.
19
+ **[📖 Docs](https://handmux.com/docs)** · **[📝 Changelog](CHANGELOG.md)** · **[📦 npm](https://www.npmjs.com/package/handmux)**
36
20
 
37
- ## Get started in 2 minutes
21
+ ## Quick start · about a minute
38
22
 
39
- You need two things **on the computer** (the phone needs only a browser). If you already live in tmux,
40
- you're basically there:
41
-
42
- ```bash
43
- node -v # need Node ≥ 18 — get it at https://nodejs.org
44
- tmux -V # need tmux ≥ 3.0 — `brew install tmux` / `apt install tmux`
45
- ```
46
-
47
- Then install and run:
23
+ On your computer you need Node 18 and tmux ≥ 3.0 (the phone just needs a browser). Then:
48
24
 
49
25
  ```bash
50
26
  npm i -g handmux # install once
51
- handmux start # run it — local / same-wifi only, nothing exposed
27
+ handmux start # run it — local / same-wifi, nothing exposed
52
28
  ```
53
29
 
54
- `start` prints a **QR code** (plus the URL and a token). **Scan the QR with your phone** — it carries
55
- the token, so you're signed in on the first load. That's it: you'll see your real tmux sessions, tap
56
- one, and you're driving it.
30
+ `start` prints a **QR code** (plus a URL and token). **Scan it with your phone** — the token rides in the code, so you're signed in on first open. You'll see your real tmux session; tap one and start driving.
57
31
 
58
- Want to reach it from **anywhere**, not just your wifi? One flag spins up a free public HTTPS link:
32
+ Want to reach it from **anywhere**? Add one flag for a free public HTTPS link:
59
33
 
60
34
  ```bash
61
- handmux start --tunnel cloudflare # instant public URL (auto-installs cloudflared)
62
- handmux setup # or configure tunnel + name + notifications once, saved
63
- ```
64
-
65
- ```
66
- tunnel cloudflare · pid 21352
67
- 🌐 open https://elementary-incidents.trycloudflare.com/
68
- 💻 local http://localhost:19999/
69
- 🔑 token aicbHOGW…
35
+ handmux start --tunnel cloudflare # instant public URL (cloudflared auto-installed)
70
36
  ```
71
37
 
72
- The printed links are token-free safe to screenshot or share. Only the **QR** carries the token, and
73
- the `🔑 token` line is your password: paste it to sign in when you open a plain link instead of scanning.
38
+ > Tunnel types, self-hosting, Windows/WSL2, and the full command & flag reference see the **[docs](https://handmux.com/docs)**.
74
39
 
75
- ### On Windows? Use WSL2
76
-
77
- handmux drives **tmux**, which is Unix-only — there's no native Windows build. Run it inside **WSL2**
78
- (a real Linux kernel with real tmux) and everything above works unchanged:
79
-
80
- ```powershell
81
- wsl --install # one-time, in PowerShell (admin): installs WSL2 + Ubuntu, then reboot
82
- ```
83
-
84
- Then open the Ubuntu terminal and follow the steps above (`apt install tmux`, install Node,
85
- `npm i -g handmux`). Two WSL-specific notes:
40
+ ## Why handmux
86
41
 
87
- - **Use a tunnel.** WSL2 is a NAT'd VM with its own IP, so the same-wifi LAN URL won't reach your
88
- phone. Start with `handmux start --tunnel cloudflare`the public link works regardless.
89
- - **Autostart needs systemd.** `handmux service` uses systemd; enable it once by adding
90
- `[boot]` / `systemd=true` to `/etc/wsl.conf`, then `wsl --shutdown`. Without it, just run
91
- `handmux start` in a terminal you keep open.
42
+ - **🧰 More than a terminal — a whole mobile vibe-coding cockpit in your pocket.** Full-screen colored git diffs, one-tap preview of a running site, docs read aloud line by line, files moved both ways a whole dev kit in hand, no hopping between apps.
43
+ - **🚀 One minute from zero to coding on your phone.** One `handmux start`, one scan, done no sign-up, no App Store, no app to sideload; just a link. "Add to Home Screen" and it's a full-screen **PWA**, as smooth as a native app.
44
+ - **🧶 Walk away, keep working.** Your phone drives the *one* live tmux pane on your desk (not a new shell, not a screenshot). Close the laptop and keep watching from your thumb — exact same state.
45
+ - **🔔 When it needs you, your phone rings.** Claude Code / Codex push the moment they need a decision; add it to your home screen and they come through as system notifications. An inbox tags each pane working / needs-you / done — many projects at a glance — and you approve permissions and plans with a tap, so you stop babysitting the screen.
46
+ - **🔒 Your code goes through no middleman.** Free and fully open-source; we run no server in the middle your data travels straight between your computer and your phone, so it stays secure.
92
47
 
93
48
  ## Features
94
49
 
95
- Not just a remote shella full **mobile cockpit** for your terminal and your coding agents.
96
-
97
- **Built around Claude Code**
98
-
99
- - **Pinged when it needs you** — a push the moment a pane hits a permission prompt, a plan approval, or finishes, even with the tab closed.
100
- - **Agent inbox** — every Claude pane tagged *working / waiting on you / done*; jump straight to the one that's blocked.
101
- - **Approve with your thumb** — answer permission prompts and plan approvals from the phone; it drives the real keys, so a tap is a real keystroke.
102
- - **Voice input** — dictate the next prompt hands-free (optional; bring your own iFlytek keys).
103
-
104
- **A real cockpit, on your phone**
50
+ - **Claude Code / Codex, deeply** an inbox status ledger, thumb-approve permissions & plans, per-agent usage bars.
51
+ - **Command & chat modes** — one bottom bar, two modes: type straight into the terminal, or talk to the agent in natural language. Preset ESC/Tab/Ctrl+C, custom ⌃⇧⌥ key-combos, and saved/recent commands split global or per-window (slash-commands included).
52
+ - **Git viewer** changes / commit history / any branch / full-screen colored diff, multi-repo tabs, read-only, never touches your tree.
53
+ - **Site preview** — a static folder, or a running service by port (routing / APIs / live-reload intact), in a phone or desktop viewport.
54
+ - **Docs** — tap a path in the terminal to open it; Markdown rendered, font zoom, sentence-by-sentence read-aloud.
55
+ - **Files both ways** — multi-select upload from the chat box, download, share in, copy any absolute path.
56
+ - **Ideas catch every one** — a thought the moment it strikes: a per-window idea/to-do list, jot one by voice and drop it straight into the prompt.
57
+ - **Built for flaky networks** — backoff reconnect, connection-lost banner, offline page, polling that pauses in the background; a reflow-safe cursor and drag-to-select copy.
58
+ - **Zero-install PWA** — runs in the browser; add to home screen for full-screen. Multilingual — English, 简体 / 繁體中文, 日本語, 한국어.
105
59
 
106
- - **Git viewer** — VS Code-style: changes, commit history, any branch, full-screen colored diffs, multi-repo tabs. Read-only, never touches your working tree.
107
- - **Live preview** — preview a static site from a folder, or a running service by port, with routing/API/HMR intact; phone or desktop viewport.
108
- - **Docs** — tap a path in the terminal to open it; Markdown rendered, font zoom, read-aloud with sentence-by-sentence highlight.
109
- - **Files both ways** — multi-select upload from the chat box (paths auto-filled), download with confirm, share into the app, copy absolute paths.
110
- - **Ideas & commands** — a per-window to-do list (voice in, one-tap insert) plus a command palette with frequent/recent and slash shortcuts (`/compact`, `/model`, `/loop`…).
111
- - **Image viewer** — pinch-zoom, save/share, inline GIFs.
112
-
113
- **Solid on a phone**
114
-
115
- - Real tmux panes — any TUI, shell or agent — not a read-only mirror.
116
- - Reconnect with backoff, a connection-lost banner, an offline fallback page, polling paused when hidden.
117
- - Reflow-safe cursor, drag-to-select copy, auto-repeat key bar, keyboard auto-lift.
118
- - Nothing to install — runs in the phone browser; optional add-to-home-screen PWA. Bilingual (English / 中文).
119
-
120
- ## Once you're in
121
-
122
- - You'll see your real tmux sessions — tap one to attach. Type in the terminal; use the on-screen
123
- key bar for arrows / Ctrl / Tab / Esc, and switch sessions, windows and panes from the top bar.
124
- - **Add to Home Screen** (Safari/Chrome share menu) to run it full-screen like an app.
125
- - The screen survives flaky networks — it keeps the last good frame, shows a "connection lost" banner
126
- after repeated failures, and pauses while the tab is hidden.
127
-
128
- ## Commands
129
-
130
- ```
131
- handmux start [flags] start server (+ tunnel), in the background
132
- handmux setup configure tunnel / name / notifications (writes config; re-run to change)
133
- handmux stop stop everything
134
- handmux restart
135
- handmux status show state + current access URL
136
- handmux logs [--follow] [--lines N] tail the supervisor log
137
- handmux config show the effective config + where each value came from
138
- handmux hooks install|uninstall enable/disable agent notifications (Claude Code / Codex inbox)
139
- handmux service install [start-flags] start at login (launchd / systemd --user)
140
- handmux service uninstall remove the autostart entry
141
- handmux update upgrade to the latest published version
142
- handmux --version print the version
143
- ```
144
-
145
- **The whole config story is two doors:** `handmux start` just runs it (no config needed — defaults to
146
- LAN-only, auto-generates a token, prints a QR), and `handmux setup` is the one place to configure
147
- persistently. Re-run `setup` to change anything. That's it; everything below is detail.
148
-
149
- ### Agent notifications (inbox)
150
-
151
- The agent inbox and "pinged when it needs you" push are driven by agent lifecycle hooks — Claude Code and
152
- Codex are both wired. They're **opt-in** — `handmux hooks install` copies a tiny notify script into
153
- `~/.claude/hooks/`, registers six hook events in `~/.claude/settings.json`, and appends a marked hooks
154
- region to `~/.codex/config.toml` if you use Codex (idempotent; leaves your own hooks alone). `handmux setup`
155
- offers this too, and you can turn it on from the phone the first time you open the inbox. `handmux hooks
156
- uninstall` removes it all. Agents you don't use are skipped — it never creates `~/.claude` or `~/.codex`.
157
-
158
- ### start flags
159
-
160
- Flags override the config file for **one run** and never persist — handy for a quick try
161
- (`handmux start --tunnel cloudflare`) without touching your saved setup. For anything permanent, use
162
- `handmux setup`.
163
-
164
- ```
165
- --tunnel none|cloudflare|cloudflare-named|ssh how to expose it (default: none — local/LAN only)
166
- --port N server port (default: 19999)
167
- --host H bind host (default: 0.0.0.0)
168
- --token S auth token (default: generated, printed on start)
169
- --name "My Box" app name in the browser tab + home-screen icon label
170
- --preview-domain D enable dynamic port previews (needs a wildcard subdomain)
171
- --config PATH use this config file instead of ~/.handmux/config.json (dev / multiple configs)
172
- --foreground, -f run in the foreground instead of daemonizing
173
- --no-qr don't render the QR code
174
-
175
- # ssh tunnel (--tunnel ssh):
176
- --ssh-host user@host[:port] the server to reverse-forward to (tunlite)
177
- --remote-port N port bound on that server (default: same as --port)
178
- --ssh-jump user@host[,…] optional bastion/jump host(s)
179
- --public-url URL the public URL to advertise (default: http://<host>:<remote-port>)
180
- # cloudflare-named (--tunnel cloudflare-named):
181
- --cf-hostname H your Cloudflare hostname (e.g. handmux.example.com)
182
- --cf-tunnel-name N named-tunnel name (default: handmux)
183
- ```
60
+ ## Networking: one decision
184
61
 
185
- ### Configuration
62
+ LAN-only by default, nothing exposed. To reach it from outside, just ask: **does your machine already have a public address?**
186
63
 
187
- There are **two layers**, and that's the whole model:
188
-
189
- - **The config file** is your machine's persistent setup (tunnel, token, push/voice keys). There is one
190
- location — `~/.handmux/config.json` — written by `handmux setup`. Pass `--config PATH` to use a
191
- different file (e.g. keep `dev.json` / `prod.json` side by side and pick one). No merging or inheritance:
192
- at most one file is read.
193
- - **Flags** override individual settings **for that one run only** and are never written back.
194
-
195
- Precedence for a setting: **flag > config file > built-in default.** `start` prints which file it loaded
196
- (`config: …`), and `handmux config` shows the value each setting resolves to **and where it came from**
197
- (flag / file / env / default), so flag-vs-file is never a mystery.
198
-
199
- You normally never hand-edit the file — `handmux setup` writes it (and re-running edits it). If you do
200
- want to, it's plain JSON; optional integrations live in the **same file** (no separate `.env`):
201
-
202
- ```jsonc
203
- {
204
- "tunnel": "none", // none | cloudflare | cloudflare-named | ssh
205
- "port": 19999,
206
- "host": "0.0.0.0",
207
- "name": "My Box", // browser-tab / home-screen label; omit → default
208
- "token": "…", // omit/empty → auto-generated on first start
209
- "previewDomain": "preview.example.com",
210
- "vapid": { "public": "…", "private": "…", "subject": "mailto:you@example.com" }, // push
211
- "xfyun": { "appId": "…", "apiKey": "…", "apiSecret": "…" } // voice
212
- // ssh tunnel adds: "sshHost", "remotePort", "sshJump", "publicUrl"
213
- // cloudflare-named adds: "cfHostname", "cfTunnelName"
214
- }
215
- ```
216
-
217
- The file is written `0600` because it holds a token and push/voice secrets.
218
-
219
- ## Networking: two paths
220
-
221
- | mode | edge | TLS / hostname | best for |
222
- |------|------|----------------|----------|
223
- | **cloudflare** | Cloudflare's global edge (free quick tunnel) | automatic, random `*.trycloudflare.com` | quick start, zero config |
224
- | **self-hosted (ssh)** | *your own VPS* | your domain + cert (Caddy auto-HTTPS recommended) | stable access, your own domain, regions where Cloudflare is unreliable |
225
-
226
- > The `ssh` self-hosted tunnel (engine: [`tunlite run`](https://www.npmjs.com/package/tunlite), bundled) is
227
- > available now — run `handmux setup` (or `--tunnel ssh --ssh-host user@host`). A `cloudflare-named` tunnel
228
- > (stable HTTPS on your own Cloudflare domain) is available the same way.
229
-
230
- ### Self-hosted ssh tunnel: server-side reverse proxy
231
-
232
- `tunlite` reverse-forwards your local port to your own server (bound to `127.0.0.1:<remote-port>` by
233
- default — not exposed to the public internet until you put a reverse proxy in front of it).
234
-
235
- **nginx (existing install):**
236
-
237
- ```nginx
238
- server {
239
- server_name handmux.example.com;
240
- client_max_body_size 60m; # prevents "file too large" on mobile uploads
241
- location / {
242
- proxy_pass http://127.0.0.1:19999; # = handmux --remote-port
243
- proxy_http_version 1.1;
244
- proxy_set_header Upgrade $http_upgrade;
245
- proxy_set_header Connection "upgrade";
246
- proxy_read_timeout 90s; # tolerates long-polling
247
- }
248
- }
249
- # Run certbot for TLS; add an A record pointing your domain at this server.
250
- ```
251
-
252
- **No nginx — Caddy (automatic Let's Encrypt, two lines):**
253
-
254
- ```caddy
255
- handmux.example.com {
256
- reverse_proxy 127.0.0.1:19999
257
- }
258
- ```
259
-
260
- No TLS needed? Bind tunlite to `0.0.0.0` and set `GatewayPorts yes` in sshd, then access via
261
- `http://<host>:<remote-port>` directly (unencrypted).
262
-
263
- ## Autostart
264
-
265
- ```bash
266
- handmux service install --tunnel cloudflare # comes back after reboot/login
267
- ```
268
-
269
- macOS uses a launchd LaunchAgent; Linux uses a `systemd --user` unit (for autostart
270
- before you log in: `loginctl enable-linger "$USER"`). While the service is installed,
271
- `handmux stop` is temporary (the OS restarts it) — use `service uninstall` to stop for good.
272
-
273
- ## Security
274
-
275
- The access URL is public when you use a tunnel, so a **token is always required** (one is
276
- generated if you don't pass `--token`). The printed plain link is token-free and safe to share;
277
- treat the token (and the token-bearing QR) like a password.
278
-
279
- Found a security issue? Please report it privately — see [SECURITY.md](SECURITY.md), not a public issue.
280
-
281
- ## Voice input (optional)
282
-
283
- Tapping the mic dictates into the input box. It's powered by [iFlytek](https://www.xfyun.cn/) and is
284
- **off until you add your own keys** — open a "语音听写 (IAT)" app at the iFlytek console, then add an
285
- `"xfyun": { "appId": "…", "apiKey": "…", "apiSecret": "…" }` block to your config file (see *Configuration*).
286
- The secret stays on the server; the phone only ever gets a short-lived signed URL. With no keys configured
287
- the mic button simply doesn't show.
288
-
289
- ## Push notifications (optional)
290
-
291
- The "pinged when a pane needs you / finishes" push is **off until you add a VAPID key pair** (the standard
292
- Web Push credential). Generate one with the bundled `web-push`:
293
-
294
- ```bash
295
- npx web-push generate-vapid-keys
296
- ```
64
+ - **Yes** (cloud box / public IP / forwarded port) — no tunnel needed, connect directly; fastest and most private.
65
+ - **No** — open a tunnel: `cloudflare` (zero-config, up in seconds, but its public nodes can be slow or unreliable in some regions) · `cloudflare-named` (your own domain, steadier) · `ssh` self-hosted (through your own server — the pick where Cloudflare is unreliable, e.g. mainland China).
297
66
 
298
- Add a `"vapid": { "public": "…", "private": "…", "subject": "mailto:you@example.com" }` block to your
299
- config file (see *Configuration*). With both keys set, `/api/push/vapid` serves the public key and the
300
- phone can subscribe; with none, the endpoint returns 503 and the bell stays hidden. Push also requires the
301
- Claude Code hooks (so a pane has a state to push) — see *Claude Code notifications* above.
67
+ > Tunnel config, server-side reverse proxy, autostart, voice/push credentials, and port previews see the **[docs](https://handmux.com/docs)**.
302
68
 
303
- ## Dynamic port previews (advanced)
69
+ ## Requirements
304
70
 
305
- Set `--preview-domain` (or `"previewDomain"` in the config) to expose other local dev
306
- servers (e.g. a Vite app on `:3000`) to your phone, each on its own subdomain. This needs a
307
- **wildcard subdomain** (`*.your.domain`) pointed at the gateway, so it only works on the
308
- self-hosted path, not a quick tunnel. `handmux setup` does not wire this for you — set it up
309
- yourself and point `previewDomain` at it.
71
+ Your computer needs **Node 18** and **tmux ≥ 3.0**; the phone just needs a browser. On **Windows**, run it inside **WSL2** (a real Linux kernel + real tmux) see the [docs](https://handmux.com/docs#windows).
310
72
 
311
- **TLS depth (Cloudflare):** a browser reaches the preview over HTTPS, so the wildcard needs a
312
- cert. Cloudflare's free Universal SSL covers **one level** — `*.example.com` works (previews at
313
- `<port>.example.com`), but a deeper `*.preview.example.com` needs **Advanced Certificate
314
- Manager**. So either keep previews one level deep, or enable ACM. (On the ssh/own-edge path you
315
- provide the wildcard cert yourself — e.g. a Let's Encrypt `*.preview.your.domain`.)
73
+ ## More
316
74
 
317
- ## License
75
+ **[📖 Docs](https://handmux.com/docs)** · **[📝 Changelog](CHANGELOG.md)** · **[🔒 Security](SECURITY.md)** · License **AGPL-3.0**
318
76
 
319
- AGPL-3.0
77
+ Found a security issue? Please report it privately (see [SECURITY.md](SECURITY.md)), not via a public issue.