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.
- package/.hop-tmux.conf +18 -0
- package/INTEGRATION.md +92 -0
- package/README.md +260 -0
- package/hay/CHANGELOG.md +46 -0
- package/hay/DEV_NOTES.md +39 -0
- package/hay/README.md +290 -0
- package/hay/apps/cli/package.json +25 -0
- package/hay/apps/cli/src/index.ts +1250 -0
- package/hay/apps/cli/tsconfig.json +8 -0
- package/hay/apps/server/package.json +37 -0
- package/hay/apps/server/src/index.ts +103 -0
- package/hay/apps/server/src/lib.ts +161 -0
- package/hay/apps/server/src/pty.ts +68 -0
- package/hay/apps/server/src/rooms.ts +421 -0
- package/hay/apps/server/src/utils.ts +20 -0
- package/hay/apps/server/test/rooms.test.ts +146 -0
- package/hay/apps/server/tsconfig.json +9 -0
- package/hay/apps/web/index.html +12 -0
- package/hay/apps/web/package.json +32 -0
- package/hay/apps/web/scripts/run-e2e.mjs +130 -0
- package/hay/apps/web/src/App.tsx +1302 -0
- package/hay/apps/web/src/components/MobileKeyboard.tsx +701 -0
- package/hay/apps/web/src/main.tsx +13 -0
- package/hay/apps/web/src/setupTests.ts +1 -0
- package/hay/apps/web/src/styles.css +1198 -0
- package/hay/apps/web/src/utils/optimisticEcho.ts +115 -0
- package/hay/apps/web/src/utils/presence.ts +24 -0
- package/hay/apps/web/src/vite-env.d.ts +10 -0
- package/hay/apps/web/test/optimisticEcho.test.ts +39 -0
- package/hay/apps/web/test/presence.test.ts +18 -0
- package/hay/apps/web/tsconfig.json +8 -0
- package/hay/apps/web/vite.config.ts +13 -0
- package/hay/apps/web/vitest.config.ts +12 -0
- package/hay/package-lock.json +5988 -0
- package/hay/package.json +18 -0
- package/hay/packages/shared/package.json +27 -0
- package/hay/packages/shared/src/colors.ts +16 -0
- package/hay/packages/shared/src/index.ts +2 -0
- package/hay/packages/shared/src/protocol.ts +90 -0
- package/hay/packages/shared/test/protocol.test.ts +33 -0
- package/hay/packages/shared/tsconfig.json +7 -0
- package/hay/tsconfig.base.json +15 -0
- package/hay-web/assets/index-CTTzApRp.css +32 -0
- package/hay-web/assets/index-FDfdolCs.js +60 -0
- package/hay-web/assets/index-FDfdolCs.js.map +1 -0
- package/hay-web/index.html +13 -0
- package/hop +3717 -0
- package/package.json +52 -0
- package/sessions.html +243 -0
- package/tunnel-shell.js +254 -0
package/hay/README.md
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
# Termshare
|
|
2
|
+
|
|
3
|
+
> **Looking for a complete remote terminal solution?** Check out [hop](https://github.com/jzthree/hop) - secure terminal access from anywhere with authentication, Cloudflare tunneling, and session management built-in.
|
|
4
|
+
|
|
5
|
+
A terminal sharing library designed as a **building block** for applications that need shared terminal access. Multiple clients (same user on different devices, or different users) can connect to the same terminal session with real-time synchronization.
|
|
6
|
+
|
|
7
|
+
**This is not a complete solution** - it provides the core terminal sharing functionality without authentication, tunneling, or session persistence. These are intentionally left to the embedding application.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
**What it provides:**
|
|
12
|
+
- **Multi-client terminal** - Multiple clients share the same PTY session
|
|
13
|
+
- **Presence indicators** - See who's connected, who's typing, who's active
|
|
14
|
+
- **Control modes** - Collaborative (all can type) or locked (single controller)
|
|
15
|
+
- **Web and CLI clients** - Connect via browser or real terminal
|
|
16
|
+
- **Embeddable library** - `attachTermshare()` to add to any HTTP server
|
|
17
|
+
- **Auto-fit** - Terminal resizes to active client's screen
|
|
18
|
+
- **Mobile-ready** - Responsive web UI with slide-out drawer and haptic feedback
|
|
19
|
+
|
|
20
|
+
**What it doesn't provide (by design):**
|
|
21
|
+
- Authentication - bring your own (see hop for example)
|
|
22
|
+
- Tunneling/remote access - bring your own (Cloudflare, ngrok, etc.)
|
|
23
|
+
- User management - clients are identified by name parameter only
|
|
24
|
+
|
|
25
|
+
## Comparison with ttyd
|
|
26
|
+
|
|
27
|
+
| Feature | Termshare | ttyd |
|
|
28
|
+
|---------|-----------|------|
|
|
29
|
+
| Web terminal | Yes | Yes |
|
|
30
|
+
| Multiple viewers | Yes | Yes |
|
|
31
|
+
| Real-time presence | Yes | No |
|
|
32
|
+
| Typing indicators | Yes | No |
|
|
33
|
+
| Control lock/collab modes | Yes | No |
|
|
34
|
+
| CLI client | Yes | No |
|
|
35
|
+
| Embeddable library | Yes | No |
|
|
36
|
+
| External dependencies | node-pty | libwebsockets |
|
|
37
|
+
|
|
38
|
+
## Quick Start
|
|
39
|
+
|
|
40
|
+
### Standalone
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# Install dependencies
|
|
44
|
+
npm install
|
|
45
|
+
|
|
46
|
+
# Start development server
|
|
47
|
+
npm run dev
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Open http://localhost:5173 and share the room link with collaborators.
|
|
51
|
+
|
|
52
|
+
### CLI Client
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# Connect to a room from your terminal
|
|
56
|
+
npm -w @termshare/cli run dev -- -r my-room -n alice
|
|
57
|
+
|
|
58
|
+
# Or after building
|
|
59
|
+
npx termshare -r my-room -n alice
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
#### Keyboard Shortcuts
|
|
63
|
+
|
|
64
|
+
The CLI client provides keyboard shortcuts for navigation and control:
|
|
65
|
+
|
|
66
|
+
| Key | Action |
|
|
67
|
+
|-----|--------|
|
|
68
|
+
| `←` `→` `↑` `↓` | Pan viewport |
|
|
69
|
+
| `0` | Center on cursor |
|
|
70
|
+
| `A` | Auto-fit viewport to content |
|
|
71
|
+
| `M` | Toggle mouse mode |
|
|
72
|
+
| `Ctrl+T` | Toggle hints bar |
|
|
73
|
+
| `Ctrl+G` | Detach from session |
|
|
74
|
+
| `Ctrl+Q` | Kill session |
|
|
75
|
+
|
|
76
|
+
#### Configuration
|
|
77
|
+
|
|
78
|
+
The CLI client reads configuration from these locations (first found wins):
|
|
79
|
+
|
|
80
|
+
1. `.hay-cli.json` (local, legacy)
|
|
81
|
+
2. `.hop.json` → `hay-cli` key (local)
|
|
82
|
+
3. `~/.hay-cli.json` (global, legacy)
|
|
83
|
+
4. `~/.hop.json` → `hay-cli` key (global)
|
|
84
|
+
|
|
85
|
+
**Example `.hop.json`:**
|
|
86
|
+
```json
|
|
87
|
+
{
|
|
88
|
+
"hay-cli": {
|
|
89
|
+
"showHints": true,
|
|
90
|
+
"scrollOff": 3
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**Options:**
|
|
96
|
+
|
|
97
|
+
| Option | Default | Description |
|
|
98
|
+
|--------|---------|-------------|
|
|
99
|
+
| `showHints` | `true` | Show keyboard shortcut hints at bottom |
|
|
100
|
+
| `scrollOff` | `3` | Lines of context to keep above/below cursor when scrolling (vim-like) |
|
|
101
|
+
|
|
102
|
+
### Web Client
|
|
103
|
+
|
|
104
|
+
The web client provides a browser-based terminal interface with:
|
|
105
|
+
|
|
106
|
+
- **Touch-friendly keyboard** - Virtual keyboard with common terminal keys
|
|
107
|
+
- **Haptic feedback** - Tactile feedback on iOS (via switch element trick) and Android (Vibration API)
|
|
108
|
+
- **Slide-out drawer** - Access presence indicators and controls on mobile
|
|
109
|
+
- **Responsive layout** - Adapts to screen size
|
|
110
|
+
|
|
111
|
+
### Embedding in Your App
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
import http from "http";
|
|
115
|
+
import { attachTermshare } from "@termshare/server";
|
|
116
|
+
|
|
117
|
+
const server = http.createServer((req, res) => {
|
|
118
|
+
res.end("Hello");
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
const termshare = attachTermshare({
|
|
122
|
+
server,
|
|
123
|
+
path: "/ws", // WebSocket endpoint
|
|
124
|
+
onConnect: (clientId, roomId, name) => {
|
|
125
|
+
console.log(`${name} joined ${roomId}`);
|
|
126
|
+
},
|
|
127
|
+
onDisconnect: (clientId, roomId) => {
|
|
128
|
+
console.log(`${clientId} left ${roomId}`);
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
server.listen(4001);
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Connect clients to `ws://localhost:4001/ws?room=ROOM&name=NAME&cols=80&rows=24`
|
|
136
|
+
|
|
137
|
+
## Architecture
|
|
138
|
+
|
|
139
|
+
```
|
|
140
|
+
┌─────────────────┐
|
|
141
|
+
│ Web Client │──┐
|
|
142
|
+
│ (xterm.js) │ │
|
|
143
|
+
└─────────────────┘ │ WebSocket ┌─────────────────┐
|
|
144
|
+
├────────────────────▶│ Termshare │
|
|
145
|
+
┌─────────────────┐ │ ?room=X&name=Y │ Server │──── node-pty ──── shell
|
|
146
|
+
│ CLI Client │──┘ │ (rooms.ts) │
|
|
147
|
+
│ (raw term) │ └─────────────────┘
|
|
148
|
+
└─────────────────┘ │
|
|
149
|
+
▼
|
|
150
|
+
┌─────────────┐
|
|
151
|
+
│ Room │
|
|
152
|
+
│ - clients │
|
|
153
|
+
│ - pty │
|
|
154
|
+
│ - collab │
|
|
155
|
+
└─────────────┘
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Protocol
|
|
159
|
+
|
|
160
|
+
Clients connect via WebSocket with query parameters:
|
|
161
|
+
- `room` - Room/session identifier
|
|
162
|
+
- `name` - Display name
|
|
163
|
+
- `cols` - Terminal columns
|
|
164
|
+
- `rows` - Terminal rows
|
|
165
|
+
|
|
166
|
+
#### Client → Server Messages
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
{ type: "input", data: string } // Terminal input
|
|
170
|
+
{ type: "resize", cols: number, rows: number } // Terminal resize
|
|
171
|
+
{ type: "typing", active: boolean } // Typing indicator
|
|
172
|
+
{ type: "toggle_collab", enabled: boolean } // Toggle collab mode
|
|
173
|
+
{ type: "take_control" } // Request control (locked mode)
|
|
174
|
+
{ type: "release_control" } // Release control
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
#### Server → Client Messages
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
{ type: "hello", clientId, roomId, color, collabMode, controllerId }
|
|
181
|
+
{ type: "output", data: string } // Terminal output
|
|
182
|
+
{ type: "snapshot", data: string } // Full terminal buffer
|
|
183
|
+
{ type: "presence", clients: [...] } // Connected users
|
|
184
|
+
{ type: "collab", enabled, controllerId } // Mode change
|
|
185
|
+
{ type: "input_rejected", reason: string } // Input denied
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Project Structure
|
|
189
|
+
|
|
190
|
+
```
|
|
191
|
+
termshare/
|
|
192
|
+
├── apps/
|
|
193
|
+
│ ├── server/ # WebSocket + PTY server (library + standalone)
|
|
194
|
+
│ ├── web/ # React + xterm.js web client
|
|
195
|
+
│ └── cli/ # Terminal client
|
|
196
|
+
└── packages/
|
|
197
|
+
└── shared/ # Protocol types and utilities
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## API Reference
|
|
201
|
+
|
|
202
|
+
### `attachTermshare(options)`
|
|
203
|
+
|
|
204
|
+
Attach termshare to an existing HTTP server.
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
type TermshareServerOptions = {
|
|
208
|
+
server: http.Server; // HTTP server to attach to
|
|
209
|
+
path?: string; // WebSocket path (default: "/ws")
|
|
210
|
+
ptyFactory?: PtyFactory; // Custom PTY factory
|
|
211
|
+
onConnect?: (clientId, roomId, name) => void;
|
|
212
|
+
onDisconnect?: (clientId, roomId) => void;
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
const termshare = attachTermshare(options);
|
|
216
|
+
|
|
217
|
+
// Returns:
|
|
218
|
+
{
|
|
219
|
+
wss: WebSocketServer; // WebSocket server instance
|
|
220
|
+
rooms: RoomManager; // Room manager instance
|
|
221
|
+
close: () => void; // Cleanup function
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### `RoomManager`
|
|
226
|
+
|
|
227
|
+
Manages terminal rooms/sessions.
|
|
228
|
+
|
|
229
|
+
```typescript
|
|
230
|
+
const rooms = new RoomManager(ptyFactory);
|
|
231
|
+
const room = rooms.getRoom(roomId, { cols: 80, rows: 24 });
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### `Room`
|
|
235
|
+
|
|
236
|
+
A single terminal session with connected clients.
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
room.attachClient(clientInfo, socketAdapter);
|
|
240
|
+
// Events: "empty" - emitted when last client leaves
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### Custom PTY Factory
|
|
244
|
+
|
|
245
|
+
Provide your own PTY implementation:
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
const customPtyFactory = ({ cols, rows, cwd }) => {
|
|
249
|
+
// Return an object implementing IPty interface:
|
|
250
|
+
// - onData(handler): Subscribe to output
|
|
251
|
+
// - write(data): Send input
|
|
252
|
+
// - resize(cols, rows): Resize terminal
|
|
253
|
+
// - kill(): Terminate
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
attachTermshare({ server, ptyFactory: customPtyFactory });
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Environment Variables
|
|
260
|
+
|
|
261
|
+
| Variable | Default | Description |
|
|
262
|
+
|----------|---------|-------------|
|
|
263
|
+
| `PORT` | 4001 | Server port |
|
|
264
|
+
| `PTY_MODE` | native | `native`, `auto`, or `mock` |
|
|
265
|
+
| `SERVE_WEB` | false | Serve web client from server |
|
|
266
|
+
| `WEB_DIST_PATH` | - | Path to web client dist |
|
|
267
|
+
|
|
268
|
+
## Production Build
|
|
269
|
+
|
|
270
|
+
```bash
|
|
271
|
+
# Build all packages
|
|
272
|
+
npm run build
|
|
273
|
+
|
|
274
|
+
# Run standalone server with embedded web client
|
|
275
|
+
PORT=4001 SERVE_WEB=true WEB_DIST_PATH=./apps/web/dist node apps/server/dist/index.js
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
## Testing
|
|
279
|
+
|
|
280
|
+
```bash
|
|
281
|
+
npm test # Run all tests
|
|
282
|
+
npm run test:unit # Unit tests only
|
|
283
|
+
npm run test:e2e # E2E tests (requires build)
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
Set `PTY_MODE=mock` for CI environments that can't spawn PTYs.
|
|
287
|
+
|
|
288
|
+
## License
|
|
289
|
+
|
|
290
|
+
MIT
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "hay-cli",
|
|
3
|
+
"version": "0.8.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"private": true,
|
|
6
|
+
"bin": {
|
|
7
|
+
"hay": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"dev": "tsx src/index.ts",
|
|
11
|
+
"build": "tsup src/index.ts --format esm"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"hay-shared": "*",
|
|
15
|
+
"xterm-headless": "^5.3.0",
|
|
16
|
+
"ws": "^8.18.0"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@types/node": "^22.10.5",
|
|
20
|
+
"@types/ws": "^8.5.12",
|
|
21
|
+
"tsup": "^8.3.5",
|
|
22
|
+
"tsx": "^4.19.2",
|
|
23
|
+
"typescript": "^5.7.3"
|
|
24
|
+
}
|
|
25
|
+
}
|