hop2 0.8.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.
Files changed (50) hide show
  1. package/.hop-tmux.conf +18 -0
  2. package/INTEGRATION.md +92 -0
  3. package/README.md +260 -0
  4. package/hay/CHANGELOG.md +46 -0
  5. package/hay/DEV_NOTES.md +39 -0
  6. package/hay/README.md +290 -0
  7. package/hay/apps/cli/package.json +25 -0
  8. package/hay/apps/cli/src/index.ts +1250 -0
  9. package/hay/apps/cli/tsconfig.json +8 -0
  10. package/hay/apps/server/package.json +37 -0
  11. package/hay/apps/server/src/index.ts +103 -0
  12. package/hay/apps/server/src/lib.ts +161 -0
  13. package/hay/apps/server/src/pty.ts +68 -0
  14. package/hay/apps/server/src/rooms.ts +421 -0
  15. package/hay/apps/server/src/utils.ts +20 -0
  16. package/hay/apps/server/test/rooms.test.ts +146 -0
  17. package/hay/apps/server/tsconfig.json +9 -0
  18. package/hay/apps/web/index.html +12 -0
  19. package/hay/apps/web/package.json +32 -0
  20. package/hay/apps/web/scripts/run-e2e.mjs +130 -0
  21. package/hay/apps/web/src/App.tsx +1302 -0
  22. package/hay/apps/web/src/components/MobileKeyboard.tsx +701 -0
  23. package/hay/apps/web/src/main.tsx +13 -0
  24. package/hay/apps/web/src/setupTests.ts +1 -0
  25. package/hay/apps/web/src/styles.css +1198 -0
  26. package/hay/apps/web/src/utils/optimisticEcho.ts +115 -0
  27. package/hay/apps/web/src/utils/presence.ts +24 -0
  28. package/hay/apps/web/src/vite-env.d.ts +10 -0
  29. package/hay/apps/web/test/optimisticEcho.test.ts +39 -0
  30. package/hay/apps/web/test/presence.test.ts +18 -0
  31. package/hay/apps/web/tsconfig.json +8 -0
  32. package/hay/apps/web/vite.config.ts +13 -0
  33. package/hay/apps/web/vitest.config.ts +12 -0
  34. package/hay/package-lock.json +5988 -0
  35. package/hay/package.json +18 -0
  36. package/hay/packages/shared/package.json +27 -0
  37. package/hay/packages/shared/src/colors.ts +16 -0
  38. package/hay/packages/shared/src/index.ts +2 -0
  39. package/hay/packages/shared/src/protocol.ts +90 -0
  40. package/hay/packages/shared/test/protocol.test.ts +33 -0
  41. package/hay/packages/shared/tsconfig.json +7 -0
  42. package/hay/tsconfig.base.json +15 -0
  43. package/hay-web/assets/index-CTTzApRp.css +32 -0
  44. package/hay-web/assets/index-FDfdolCs.js +60 -0
  45. package/hay-web/assets/index-FDfdolCs.js.map +1 -0
  46. package/hay-web/index.html +13 -0
  47. package/hop +3717 -0
  48. package/package.json +52 -0
  49. package/sessions.html +243 -0
  50. package/tunnel-shell.js +254 -0
package/.hop-tmux.conf ADDED
@@ -0,0 +1,18 @@
1
+
2
+ # Hop Terminal - Tmux Configuration
3
+ # Do not use user's config - this is hop-managed
4
+
5
+ # Enable mouse support
6
+ set -g mouse on
7
+
8
+ # 2-line scroll speed (compromise between smoothness and remote latency)
9
+ bind -T copy-mode WheelUpPane send -N 2 -X scroll-up
10
+ bind -T copy-mode WheelDownPane send -N 2 -X scroll-down
11
+ bind -T copy-mode-vi WheelUpPane send -N 2 -X scroll-up
12
+ bind -T copy-mode-vi WheelDownPane send -N 2 -X scroll-down
13
+
14
+ # Increase scrollback buffer
15
+ set -g history-limit 50000
16
+
17
+ # Better terminal colors
18
+ set -g default-terminal "screen-256color"
package/INTEGRATION.md ADDED
@@ -0,0 +1,92 @@
1
+ # Hop + Termshare Integration Plan
2
+
3
+ This branch integrates [termshare](https://github.com/jzthree/termshare) to replace ttyd/tmux.
4
+
5
+ ## Benefits
6
+
7
+ - **No ttyd/tmux dependencies** - Pure Node.js solution
8
+ - **Built-in presence** - See who's connected, typing indicators
9
+ - **Control modes** - Collaborative or locked single-controller
10
+ - **No injection hacks** - Direct control over web UI
11
+ - **Mobile-ready** - Responsive design with iOS-style keyboard built-in
12
+ - **CLI client** - Connect from real terminals too
13
+
14
+ ## Architecture Change
15
+
16
+ ### Before (ttyd)
17
+ ```
18
+ Browser → Hop Auth Proxy → ttyd (per session) → tmux → shell
19
+ ↓
20
+ HTML/WebSocket injection for mobile keyboard
21
+ ```
22
+
23
+ ### After (termshare)
24
+ ```
25
+ Browser → Hop Auth Proxy → Termshare WebSocket → node-pty → shell
26
+ ↓
27
+ Native termshare web UI (mobile keyboard built-in)
28
+ ```
29
+
30
+ ## Implementation Steps
31
+
32
+ ### 1. Add Dependencies
33
+ - [x] `node-pty-prebuilt-multiarch` - PTY handling
34
+ - [x] `ws` - WebSocket server
35
+ - [x] Copy termshare room/pty logic into hop (`termshare.js`)
36
+
37
+ ### 2. Remove ttyd/tmux Code
38
+ - [x] Remove `spawn('ttyd', [...])` in `getOrCreateSession()`
39
+ - [ ] Remove `HOP_TMUX_CONF`, `HOP_TMUX_WRAPPER` generation (still present but unused)
40
+ - [ ] Remove `listTmuxSessions()`, `waitForTmuxSessionReady()` (still present but unused)
41
+ - [ ] Remove ttyd dependency check (still present but unused)
42
+ - [x] Remove keyboard HTML injection code (now using termshare's built-in)
43
+ - [x] Remove `getIOSKeyboardInjection()` WebSocket patching (still present but unused)
44
+
45
+ ### 3. Add Termshare Integration
46
+ - [x] Port `Room` and `RoomManager` classes from termshare
47
+ - [x] Port `createPty` function
48
+ - [x] Create termshare WebSocket handler at `/ws` endpoint
49
+ - [x] Map hop sessions to termshare rooms
50
+
51
+ ### 4. Update Session Management
52
+ ```javascript
53
+ // Before: activeSessions[name] = { ttyd: Process, port: number }
54
+ // After: activeSessions[name] = { room: Room }
55
+ ```
56
+ ✅ Completed
57
+
58
+ ### 5. Serve Termshare Web UI
59
+ - [x] Build termshare web client (with mobile keyboard)
60
+ - [x] Serve static files from hop (`termshare-web/` directory)
61
+ - [x] Inject room parameter for session routing
62
+
63
+ ### 6. Keep Hop Features
64
+ - [x] Authentication (TOTP + cookies)
65
+ - [x] Cloudflare tunneling
66
+ - [x] Session URL routing (`/s/<name>/`)
67
+ - [x] Multi-user support
68
+
69
+ ## Key Files Changed
70
+
71
+ - `hop` - Main server, updated `getOrCreateSession()` and WebSocket handler
72
+ - `termshare.js` - New module with Room, RoomManager, createPty
73
+ - `termshare-web/` - Built termshare web client with mobile keyboard
74
+
75
+ ## Testing
76
+
77
+ 1. Start hop: `node hop`
78
+ 2. Open browser to tunnel URL
79
+ 3. Verify:
80
+ - [ ] Login with TOTP works
81
+ - [ ] Terminal session works
82
+ - [ ] Multiple clients see same session
83
+ - [ ] Presence indicators show
84
+ - [ ] Control modes work
85
+ - [ ] Mobile UI works without injection
86
+
87
+ ## Migration Notes
88
+
89
+ - Sessions are not persistent (by design) - shell exits when all clients disconnect
90
+ - tmux scrollback replaced by termshare's output buffer (200KB)
91
+ - iOS keyboard injection replaced by termshare's built-in mobile keyboard
92
+ - The old ttyd/tmux code paths are still present but no longer used; can be removed in a cleanup pass
package/README.md ADDED
@@ -0,0 +1,260 @@
1
+ # 🐰 hop
2
+
3
+ **Hop into your terminal from anywhere in the world.**
4
+
5
+ Access your Mac's terminal from your phone, tablet, or any browser — secured with password + 2FA, tunneled through Cloudflare.
6
+
7
+ > **🍎 macOS/Linux** — Requires cloudflared for tunneling
8
+
9
+ ```
10
+ (\(\
11
+ ( -.-) "hop into your shell"
12
+ o_(")(")
13
+ ```
14
+
15
+ ## ✨ Features
16
+
17
+ - 🔐 **Password + 2FA** — Optional password plus TOTP (required for custom domains)
18
+ - 🌍 **Access Anywhere** — Cloudflare tunnel, no port forwarding
19
+ - 🌐 **Custom Domains + Multi‑User** — Share subdomains with per‑user credentials
20
+ - 📱 **Mobile Virtual Keyboard** — Custom keyboard with Esc, Ctrl, Alt, arrows, and more
21
+ - ⌨️ **Native Keyboard Support** — Tap the blue button for dictation, spellcheck & autocomplete
22
+ - 🪟 **Multi-Session** — Create and switch between named sessions
23
+ - 🔌 **Port Sessions** — Proxy a local HTTP/WS service via hop
24
+ - 🔄 **Quick Session Switching** — Floating menu to switch sessions without leaving the terminal
25
+ - 🎨 **Modern UI** — Clean, minimal iOS-style design
26
+ - ⚡ **Auto-Attach** — Multiple terminals share the same tunnel
27
+
28
+ ## 🚀 Quick Install
29
+
30
+ ### From npm (easiest)
31
+
32
+ ```bash
33
+ npm install -g hop2
34
+ ```
35
+
36
+ Then just run:
37
+ ```bash
38
+ hop
39
+ ```
40
+
41
+ ### One-liner from source
42
+
43
+ ```bash
44
+ git clone https://github.com/jzthree/hop.git ~/.hop && \
45
+ cd ~/.hop && npm install && \
46
+ sudo ln -sf ~/.hop/hop /usr/local/bin/hop
47
+ ```
48
+
49
+ ### Manual Install from source
50
+
51
+ ```bash
52
+ # Clone the repo
53
+ git clone https://github.com/jzthree/hop.git ~/.hop
54
+
55
+ # Install dependencies
56
+ cd ~/.hop && npm install
57
+
58
+ # Add to PATH (pick one)
59
+ # Option A: Symlink (requires sudo)
60
+ sudo ln -sf ~/.hop/hop /usr/local/bin/hop
61
+
62
+ # Option B: Add to PATH
63
+ echo 'export PATH="$HOME/.hop:$PATH"' >> ~/.zshrc
64
+ source ~/.zshrc
65
+ ```
66
+
67
+ ## 🐰 Usage
68
+
69
+ ```bash
70
+ # Start hopping!
71
+ hop
72
+
73
+ # Use iTerm control mode for local session
74
+ hop --iterm
75
+ ```
76
+
77
+ **First time:**
78
+ 1. (Optional) Set a password: `hop password set`
79
+ 2. Scan the QR code with your authenticator app (Google Authenticator, Authy, 1Password, etc.)
80
+ 3. Note the URL displayed
81
+ 4. Press Enter to start your local session
82
+
83
+ **From your phone:**
84
+ 1. Open the URL in your browser
85
+ 2. Enter your password (if enabled) and 6‑digit code
86
+ 3. Pick or create a session
87
+ 4. 🐰 You're in! Use the virtual keyboard for terminal keys, or tap the blue button for native input
88
+
89
+ ## 🪟 Sessions
90
+
91
+ Create multiple independent terminal sessions from the Session Picker (`/sessions`):
92
+
93
+ - **Create**: Type a name and click Create
94
+ - **Join**: Click Join on any existing session
95
+ - **Sessions are shared**: Multiple devices can view the same session
96
+
97
+ You can also create **Port Sessions** that proxy to a local HTTP/WS service on your machine.
98
+ Use the Session Picker or:
99
+ ```bash
100
+ hop session add myapp --port 3000
101
+ ```
102
+
103
+ ## 🌐 Custom Domains & Multi‑User
104
+
105
+ ### Admin (your machine)
106
+ 1. Set a password (required for custom domains):
107
+ ```bash
108
+ hop password set
109
+ ```
110
+ 2. Configure your domain:
111
+ ```bash
112
+ hop domain hop.yourdomain.com
113
+ ```
114
+ 3. Add a user + export credentials:
115
+ ```bash
116
+ hop user add alice
117
+ hop user export alice
118
+ ```
119
+ 4. Send the exported folder to the user.
120
+
121
+ ### User (their machine)
122
+ ```bash
123
+ npm install -g hop2
124
+ hop client ./credentials.json
125
+ ```
126
+ First run prompts them to set a password + scan a TOTP QR code.
127
+ They then log in at their URL, e.g. `https://alice.hop.yourdomain.com`.
128
+
129
+ ## 📱 Mobile Keyboard
130
+
131
+ On mobile devices, Hop provides a custom virtual keyboard designed for terminal use:
132
+
133
+ **Accessory Row:**
134
+ - `Esc` `Tab` `Ctrl` `Alt` — Essential terminal keys
135
+ - `← ↓ ↑ →` — Arrow keys for navigation
136
+ - 🔵 **Blue keyboard button** — Opens native iOS keyboard for dictation, spellcheck & autocomplete
137
+
138
+ **Floating Menu (top-right button):**
139
+ - **Toggle Keyboard** — Show/hide the virtual keyboard
140
+ - **Session List** — Quick-switch between sessions
141
+ - **All Sessions** — Return to session picker
142
+
143
+ **Tips:**
144
+ - The floating button is draggable — move it anywhere
145
+ - First-time users will see a tooltip pointing to the native keyboard button
146
+ - Use the native keyboard for longer text input with autocomplete
147
+
148
+ ## 🖥️ iTerm Integration
149
+
150
+ ```bash
151
+ hop --iterm
152
+ ```
153
+
154
+ Uses tmux control mode (`-CC`) for native scrolling, copy/paste, splits, and search. Session remains accessible via web.
155
+
156
+ ## 🔌 Port Sessions
157
+
158
+ Expose local HTTP/WebSocket services through your tunnel:
159
+
160
+ ```bash
161
+ hop session add myapp --port 3000
162
+ # Access at: https://your-tunnel-url/s/myapp/
163
+ ```
164
+
165
+ Works with dev servers, Jupyter, APIs — anything on localhost. Supports WebSocket.
166
+
167
+ ## 🔧 Commands
168
+
169
+ | Command | Description |
170
+ |---------|-------------|
171
+ | `hop` | Start hop (or attach to existing tunnel) |
172
+ | `hop --iterm` | Use iTerm tmux control mode for local session |
173
+ | `hop url` | Print current tunnel URL |
174
+ | `hop qr` | Show QR code for current URL |
175
+ | `hop domain <hostname>` | Set custom domain (named tunnel) |
176
+ | `hop domain-clear` | Remove custom domain, use random URLs |
177
+ | `hop password set` | Set/change password |
178
+ | `hop password clear` | Remove password protection |
179
+ | `hop user list` | List users |
180
+ | `hop user add <name>` | Add user + subdomain |
181
+ | `hop user remove <name>` | Remove user |
182
+ | `hop user export <name>` | Export user credentials |
183
+ | `hop session list` | List sessions |
184
+ | `hop session add <name>` | Create a terminal session |
185
+ | `hop session add <name> --port N` | Create a port session (proxy) |
186
+ | `hop session remove <name>` | Remove a session |
187
+ | `hop client <credentials>` | Run hop with exported credentials |
188
+ | `hop wipe` | Kill all hop tmux sessions |
189
+ | `quit` | Type at exit prompt to shutdown tunnel |
190
+
191
+ ## 📦 Dependencies
192
+
193
+ Installed automatically via Homebrew:
194
+ - `tmux` — Terminal multiplexer
195
+ - `ttyd` — Web terminal
196
+ - `cloudflared` — Cloudflare tunnel
197
+
198
+ Node.js packages:
199
+ - `http-proxy` — Request proxying
200
+ - `otplib` — TOTP authentication
201
+ - `qrcode-terminal` — QR code display
202
+ - `cookie` — Session cookies
203
+
204
+ ## 🛡️ Security
205
+
206
+ - **Password + TOTP** — Password optional, but required for custom domains
207
+ - **Rate Limiting** — Exponential backoff on failed attempts
208
+ - **Secure Cookies** — `httpOnly`, `secure`, `sameSite=lax`
209
+ - **Random URL** — Unguessable tunnel URL for quick tunnels
210
+ - **Fixed URL** — Custom domains keep a stable URL
211
+ - **Local Binding** — Server only listens on 127.0.0.1
212
+ - **End-to-End TLS** — Cloudflare Tunnel encryption
213
+
214
+ **Passwords:** Recommended for any public URL; required for custom domains.
215
+
216
+ **Secrets:** Stored in `~/.hop2/` (treat like `~/.ssh/`)
217
+
218
+ ## 🛠 Development (Hay)
219
+
220
+ The hay source is vendored in `./hay`. Hop will serve `hay/apps/web/dist` when present.
221
+
222
+ ```bash
223
+ npm run build
224
+ ```
225
+
226
+ This builds hay (web + cli) and syncs `hay/apps/web/dist` into `hay-web/` for Hop to serve.
227
+ If `./hay` exists and the dist folder is missing, `hop` will auto-build hay on startup.
228
+
229
+ ## 🐛 Troubleshooting
230
+
231
+ **QR code not working?**
232
+ Delete `.auth_secret` and restart hop to generate a new code.
233
+
234
+ **Client reset (user mode)?**
235
+ Delete `~/.hop2/clients/<tunnel-id>/` and run `hop client` again.
236
+
237
+ **Tunnel not starting?**
238
+ Make sure `cloudflared` is installed: `brew install cloudflared`
239
+
240
+ **Stuck processes?**
241
+ ```bash
242
+ pkill ttyd; pkill cloudflared; tmux kill-server
243
+ ```
244
+
245
+ ## 📝 License
246
+
247
+ MIT
248
+
249
+ ---
250
+
251
+ Made with 🐰 for hopping around
252
+
253
+ ```
254
+ ____
255
+ / \
256
+ | ^ ^ | hop hop hop
257
+ | .. |
258
+ \ -- /
259
+ ||||
260
+ ```
@@ -0,0 +1,46 @@
1
+ # Changelog
2
+
3
+ ## [0.8.0] - 2025-01-25
4
+
5
+ ### Major Changes
6
+
7
+ - **Replaced ttyd/tmux with hay/node-pty**: Terminal sessions are now managed directly via node-pty instead of ttyd+tmux. This removes external dependencies and provides native multi-client support, presence indicators, and better web/CLI integration.
8
+
9
+ ### Breaking Changes
10
+
11
+ - **RoomManager.getRoom() signature changed**: Now accepts optional third `cwd` parameter
12
+ ```typescript
13
+ // Before
14
+ getRoom(roomId: string, initialSize: { cols: number; rows: number })
15
+
16
+ // After
17
+ getRoom(roomId: string, initialSize: { cols: number; rows: number }, cwd?: string)
18
+ ```
19
+ The per-room `cwd` takes precedence over the RoomManager constructor's default cwd.
20
+
21
+ ### New Features
22
+
23
+ #### CLI Client
24
+ - **Keyboard shortcuts**: Pan (arrows), center on cursor (0), auto-fit (A), mouse toggle (M), hints (Ctrl+T), detach (Ctrl+G), kill (Ctrl+Q)
25
+ - **Configuration file support**: Reads from `.hop.json` (under `hay-cli` key) or legacy `.hay-cli.json`
26
+ - **Config hierarchy**: Local config takes precedence over global (`~/.hop.json`)
27
+ - **scrollOff setting**: Vim-like scroll margin (default: 3 lines) keeps context visible around cursor
28
+ - **showHints setting**: Toggle keyboard shortcut hints bar
29
+
30
+ #### Web Client
31
+ - **Haptic feedback**: Tactile feedback on mobile keyboard (iOS via switch element trick, Android via Vibration API)
32
+ - **Improved mobile layout**: Responsive drawer and keyboard
33
+
34
+ ### Bug Fixes
35
+
36
+ - Fixed sessions always starting in daemon's directory instead of caller's pwd
37
+ - Fixed cursor scroll behavior to match regular terminals (only scroll when cursor exits viewport)
38
+ - Fixed initial viewport positioning on session start
39
+
40
+ ## [0.1.0] - Initial Release
41
+
42
+ - Multi-client terminal sharing
43
+ - Web and CLI clients
44
+ - Presence indicators and typing status
45
+ - Collaborative and locked control modes
46
+ - Embeddable server library
@@ -0,0 +1,39 @@
1
+ # Dev Notes
2
+
3
+ ## Mobile touch scrolling + selection mode (iOS Safari)
4
+
5
+ We intentionally disable native touch handling on the xterm DOM so we can
6
+ implement our own smooth touch scrolling, but we also need a "selection mode"
7
+ that restores iOS text selection handles. The fix is split across CSS and JS.
8
+
9
+ ### Default mode (scrolling)
10
+ * **CSS** (`hay/apps/web/src/styles.css`):
11
+ * `.terminal-scroll` and `.xterm*` get `touch-action: none` to prevent
12
+ Safari's native scrolling/selection on the terminal surface.
13
+ * `.xterm-rows` and span children use `pointer-events: none` so touches hit
14
+ our container instead of individual text nodes.
15
+ * **JS** (`hay/apps/web/src/App.tsx`, mobile touch effect):
16
+ * We attach custom `touchstart/move/end` handlers to implement momentum
17
+ scrolling on the terminal content.
18
+
19
+ ### Selection mode (text selection)
20
+ * **CSS** (`hay/apps/web/src/styles.css`):
21
+ * `.session.selection-mode` re-enables `pointer-events` and `user-select`
22
+ on `.xterm-rows` so iOS can show selection handles.
23
+ * `touch-action: auto` is restored on xterm elements.
24
+ * **JS** (`hay/apps/web/src/App.tsx`):
25
+ * We skip the custom touch scrolling effect when `selectionMode` is true.
26
+ * We disable xterm's internal `viewport.handleTouchStart/Move` to stop it
27
+ from swallowing selection gestures.
28
+ * The click handler clears selection and toggles `user-select` to re-arm
29
+ iOS selection after a tap.
30
+
31
+ ### If selection or scrolling regresses
32
+ 1. Confirm `.session` has the `selection-mode` class when the toggle is on.
33
+ 2. Verify the CSS block for `.session.selection-mode` is present (pointer-events
34
+ and user-select must be re-enabled).
35
+ 3. In `App.tsx`, ensure the selection-mode guards still:
36
+ * prevent installing custom touch scrolling, and
37
+ * override `viewport.handleTouchStart/Move`.
38
+ 4. If text selection works but scrolling is broken, verify `touch-action: none`
39
+ and `pointer-events: none` are still applied in default mode.