handmux 0.8.0 → 0.9.1

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
@@ -2,316 +2,76 @@
2
2
 
3
3
  **[handmux.com](https://handmux.com)** · *[中文文档 → README.zh-CN.md](README.zh-CN.md)*
4
4
 
5
- [![npm](https://img.shields.io/npm/v/handmux?color=cb3837&logo=npm)](https://www.npmjs.com/package/handmux) [![CI](https://github.com/yuanyuanzijin/handmux/actions/workflows/test.yml/badge.svg)](https://github.com/yuanyuanzijin/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)
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
- <img src="assets/handmux-approve.gif" alt="handmux: a Claude Code permission lands on your phone and you approve it with a tap" width="280">
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">
21
13
  &nbsp;&nbsp;
22
- <img src="assets/handmux-git.gif" alt="handmux: open a doc as rendered Markdown, then read a git diff full-screen" width="280">
14
+ <img src="assets/handmux-doc.gif" alt="handmux: a push pings you when a pane needs you, and you review the git repo and each agent's usage" width="280">
23
15
  <br>
24
- <em>Real phone browser, real panes — a push pings you to approve a Claude Code prompt (left); read a doc as Markdown (even aloud) &amp; review a git diff (right).</em>
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.** Deepest with Claude Code: a push the instant it needs you,
34
- an inbox of which pane is *working / waiting on you / done*, and plan & permission approvals you tap
35
- through. Codex, aider, any shell/TUI work too.
19
+ **[📖 Docs](https://handmux.com/docs.html)** · **[📝 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.html)**.
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 Claude Code notifications (inbox)
139
- handmux service install [start-flags] start at login (launchd / systemd --user)
140
- handmux service uninstall remove the autostart entry
141
- handmux --version print the version
142
- ```
143
-
144
- **The whole config story is two doors:** `handmux start` just runs it (no config needed — defaults to
145
- LAN-only, auto-generates a token, prints a QR), and `handmux setup` is the one place to configure
146
- persistently. Re-run `setup` to change anything. That's it; everything below is detail.
147
-
148
- ### Claude Code notifications (inbox)
149
-
150
- The agent inbox and "pinged when it needs you" push are driven by Claude Code lifecycle hooks. They're
151
- **opt-in** — `handmux hooks install` copies a tiny notify script into `~/.claude/hooks/` and registers six
152
- hook events in `~/.claude/settings.json` (idempotent; leaves your own hooks alone). `handmux setup` offers
153
- this too, and you can turn it on from the phone the first time you open the inbox. `handmux hooks uninstall`
154
- removes it. If you don't use Claude Code, this is skipped — nothing touches `~/.claude`.
155
-
156
- ### start flags
157
-
158
- Flags override the config file for **one run** and never persist — handy for a quick try
159
- (`handmux start --tunnel cloudflare`) without touching your saved setup. For anything permanent, use
160
- `handmux setup`.
161
-
162
- ```
163
- --tunnel none|cloudflare|cloudflare-named|ssh how to expose it (default: none — local/LAN only)
164
- --port N server port (default: 19999)
165
- --host H bind host (default: 0.0.0.0)
166
- --token S auth token (default: generated, printed on start)
167
- --name "My Box" app name in the browser tab + home-screen icon label
168
- --preview-domain D enable dynamic port previews (needs a wildcard subdomain)
169
- --config PATH use this config file instead of ~/.handmux/config.json (dev / multiple configs)
170
- --foreground, -f run in the foreground instead of daemonizing
171
- --no-qr don't render the QR code
172
-
173
- # ssh tunnel (--tunnel ssh):
174
- --ssh-host user@host[:port] the server to reverse-forward to (tunlite)
175
- --remote-port N port bound on that server (default: same as --port)
176
- --ssh-jump user@host[,…] optional bastion/jump host(s)
177
- --public-url URL the public URL to advertise (default: http://<host>:<remote-port>)
178
- # cloudflare-named (--tunnel cloudflare-named):
179
- --cf-hostname H your Cloudflare hostname (e.g. handmux.example.com)
180
- --cf-tunnel-name N named-tunnel name (default: handmux)
181
- ```
60
+ ## Networking: one decision
182
61
 
183
- ### Configuration
62
+ LAN-only by default, nothing exposed. To reach it from outside, just ask: **does your machine already have a public address?**
184
63
 
185
- There are **two layers**, and that's the whole model:
186
-
187
- - **The config file** is your machine's persistent setup (tunnel, token, push/voice keys). There is one
188
- location — `~/.handmux/config.json` — written by `handmux setup`. Pass `--config PATH` to use a
189
- different file (e.g. keep `dev.json` / `prod.json` side by side and pick one). No merging or inheritance:
190
- at most one file is read.
191
- - **Flags** override individual settings **for that one run only** and are never written back.
192
-
193
- Precedence for a setting: **flag > config file > built-in default.** `start` prints which file it loaded
194
- (`config: …`), and `handmux config` shows the value each setting resolves to **and where it came from**
195
- (flag / file / env / default), so flag-vs-file is never a mystery.
196
-
197
- You normally never hand-edit the file — `handmux setup` writes it (and re-running edits it). If you do
198
- want to, it's plain JSON; optional integrations live in the **same file** (no separate `.env`):
199
-
200
- ```jsonc
201
- {
202
- "tunnel": "none", // none | cloudflare | cloudflare-named | ssh
203
- "port": 19999,
204
- "host": "0.0.0.0",
205
- "name": "My Box", // browser-tab / home-screen label; omit → default
206
- "token": "…", // omit/empty → auto-generated on first start
207
- "previewDomain": "preview.example.com",
208
- "vapid": { "public": "…", "private": "…", "subject": "mailto:you@example.com" }, // push
209
- "xfyun": { "appId": "…", "apiKey": "…", "apiSecret": "…" } // voice
210
- // ssh tunnel adds: "sshHost", "remotePort", "sshJump", "publicUrl"
211
- // cloudflare-named adds: "cfHostname", "cfTunnelName"
212
- }
213
- ```
214
-
215
- The file is written `0600` because it holds a token and push/voice secrets.
216
-
217
- ## Networking: two paths
218
-
219
- | mode | edge | TLS / hostname | best for |
220
- |------|------|----------------|----------|
221
- | **cloudflare** | Cloudflare's global edge (free quick tunnel) | automatic, random `*.trycloudflare.com` | quick start, zero config |
222
- | **self-hosted (ssh)** | *your own VPS* | your domain + cert (Caddy auto-HTTPS recommended) | stable access, your own domain, regions where Cloudflare is unreliable |
223
-
224
- > The `ssh` self-hosted tunnel (engine: [`tunlite run`](https://www.npmjs.com/package/tunlite), bundled) is
225
- > available now — run `handmux setup` (or `--tunnel ssh --ssh-host user@host`). A `cloudflare-named` tunnel
226
- > (stable HTTPS on your own Cloudflare domain) is available the same way.
227
-
228
- ### Self-hosted ssh tunnel: server-side reverse proxy
229
-
230
- `tunlite` reverse-forwards your local port to your own server (bound to `127.0.0.1:<remote-port>` by
231
- default — not exposed to the public internet until you put a reverse proxy in front of it).
232
-
233
- **nginx (existing install):**
234
-
235
- ```nginx
236
- server {
237
- server_name handmux.example.com;
238
- client_max_body_size 60m; # prevents "file too large" on mobile uploads
239
- location / {
240
- proxy_pass http://127.0.0.1:19999; # = handmux --remote-port
241
- proxy_http_version 1.1;
242
- proxy_set_header Upgrade $http_upgrade;
243
- proxy_set_header Connection "upgrade";
244
- proxy_read_timeout 90s; # tolerates long-polling
245
- }
246
- }
247
- # Run certbot for TLS; add an A record pointing your domain at this server.
248
- ```
249
-
250
- **No nginx — Caddy (automatic Let's Encrypt, two lines):**
251
-
252
- ```caddy
253
- handmux.example.com {
254
- reverse_proxy 127.0.0.1:19999
255
- }
256
- ```
257
-
258
- No TLS needed? Bind tunlite to `0.0.0.0` and set `GatewayPorts yes` in sshd, then access via
259
- `http://<host>:<remote-port>` directly (unencrypted).
260
-
261
- ## Autostart
262
-
263
- ```bash
264
- handmux service install --tunnel cloudflare # comes back after reboot/login
265
- ```
266
-
267
- macOS uses a launchd LaunchAgent; Linux uses a `systemd --user` unit (for autostart
268
- before you log in: `loginctl enable-linger "$USER"`). While the service is installed,
269
- `handmux stop` is temporary (the OS restarts it) — use `service uninstall` to stop for good.
270
-
271
- ## Security
272
-
273
- The access URL is public when you use a tunnel, so a **token is always required** (one is
274
- generated if you don't pass `--token`). The printed plain link is token-free and safe to share;
275
- treat the token (and the token-bearing QR) like a password.
276
-
277
- Found a security issue? Please report it privately — see [SECURITY.md](SECURITY.md), not a public issue.
278
-
279
- ## Voice input (optional)
280
-
281
- Tapping the mic dictates into the input box. It's powered by [iFlytek](https://www.xfyun.cn/) and is
282
- **off until you add your own keys** — open a "语音听写 (IAT)" app at the iFlytek console, then add an
283
- `"xfyun": { "appId": "…", "apiKey": "…", "apiSecret": "…" }` block to your config file (see *Configuration*).
284
- The secret stays on the server; the phone only ever gets a short-lived signed URL. With no keys configured
285
- the mic button simply doesn't show.
286
-
287
- ## Push notifications (optional)
288
-
289
- The "pinged when a pane needs you / finishes" push is **off until you add a VAPID key pair** (the standard
290
- Web Push credential). Generate one with the bundled `web-push`:
291
-
292
- ```bash
293
- npx web-push generate-vapid-keys
294
- ```
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).
295
66
 
296
- Add a `"vapid": { "public": "…", "private": "…", "subject": "mailto:you@example.com" }` block to your
297
- config file (see *Configuration*). With both keys set, `/api/push/vapid` serves the public key and the
298
- phone can subscribe; with none, the endpoint returns 503 and the bell stays hidden. Push also requires the
299
- 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.html)**.
300
68
 
301
- ## Dynamic port previews (advanced)
69
+ ## Requirements
302
70
 
303
- Set `--preview-domain` (or `"previewDomain"` in the config) to expose other local dev
304
- servers (e.g. a Vite app on `:3000`) to your phone, each on its own subdomain. This needs a
305
- **wildcard subdomain** (`*.your.domain`) pointed at the gateway, so it only works on the
306
- self-hosted path, not a quick tunnel. `handmux setup` does not wire this for you — set it up
307
- 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.html#windows).
308
72
 
309
- **TLS depth (Cloudflare):** a browser reaches the preview over HTTPS, so the wildcard needs a
310
- cert. Cloudflare's free Universal SSL covers **one level** — `*.example.com` works (previews at
311
- `<port>.example.com`), but a deeper `*.preview.example.com` needs **Advanced Certificate
312
- Manager**. So either keep previews one level deep, or enable ACM. (On the ssh/own-edge path you
313
- provide the wildcard cert yourself — e.g. a Let's Encrypt `*.preview.your.domain`.)
73
+ ## More
314
74
 
315
- ## License
75
+ **[📖 Docs](https://handmux.com/docs.html)** · **[📝 Changelog](CHANGELOG.md)** · **[🔒 Security](SECURITY.md)** · License **AGPL-3.0**
316
76
 
317
- AGPL-3.0
77
+ Found a security issue? Please report it privately (see [SECURITY.md](SECURITY.md)), not via a public issue.