ghostterm 1.1.0 โ†’ 1.1.2

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 (3) hide show
  1. package/README.md +199 -66
  2. package/bin/ghostterm.js +34 -6
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,37 +1,34 @@
1
- # GhostTerm ๐Ÿ‘ป
2
-
3
- **A mobile terminal for Claude Code fans.** Control your PC from your phone โ€” real CLI, not a toy.
4
-
5
- > *GhostTerm is not affiliated with Anthropic. Just a fan who couldn't stop using Claude Code.*
6
-
7
- ---
8
-
9
- ## Screenshots
10
-
11
1
  <p align="center">
12
- <img src="https://ghostterm.pages.dev/img/claude-code.jpg" width="250" alt="Claude Code running on phone">
13
- <img src="https://ghostterm.pages.dev/img/terminal.jpg" width="250" alt="Terminal with dangerous mode">
14
- <img src="https://ghostterm.pages.dev/img/pixel-office.jpg" width="250" alt="4 terminal sessions">
2
+ <img src="https://ghostterm.pages.dev/img/banner.png" width="500" alt="GhostTerm Banner">
15
3
  </p>
16
4
 
5
+ <h1 align="center">GhostTerm</h1>
6
+ <p align="center">
7
+ <strong>A mobile terminal for Claude Code fans.</strong><br>
8
+ Control your PC from your phone โ€” real CLI, not a toy.
9
+ </p>
17
10
  <p align="center">
18
- <em>Left: Claude Code with <code>--dangerously-skip-permissions</code> running on your phone</em><br>
19
- <em>Center: Full terminal + custom shortcut keys (y/n, Tab, Ctrl+C, arrows...)</em><br>
20
- <em>Right: 4 ghost terminals in pixel office view</em>
11
+ <a href="https://www.npmjs.com/package/ghostterm"><img src="https://img.shields.io/npm/v/ghostterm.svg" alt="npm version"></a>
12
+ <a href="https://www.npmjs.com/package/ghostterm"><img src="https://img.shields.io/npm/dm/ghostterm.svg" alt="npm downloads"></a>
13
+ <img src="https://img.shields.io/node/v/ghostterm" alt="node version">
14
+ <img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="license">
21
15
  </p>
22
16
 
17
+ > *GhostTerm is not affiliated with Anthropic. Just a fan who couldn't stop using Claude Code.*
18
+
23
19
  ---
24
20
 
25
- ## Why GhostTerm?
21
+ ## What is GhostTerm?
22
+
23
+ GhostTerm lets you **control your PC terminal from your phone** over the internet. It's built specifically for [Claude Code](https://docs.anthropic.com/en/docs/claude-code) users who want to:
26
24
 
27
- You're running Claude Code on your PC. You walk away. Now what?
25
+ - Run `claude --dangerously-skip-permissions` and monitor everything from their phone
26
+ - Approve/deny tool calls from anywhere (bus, bed, coffee shop)
27
+ - Manage 4 terminal sessions simultaneously
28
28
 
29
- - **Run `--dangerously-skip-permissions`** โ€” let Claude go full auto, monitor from your phone while it codes for you
30
- - **4 terminal sessions at once** โ€” more than Anthropic's official app
31
- - **Real xterm.js terminal** โ€” full ANSI colors, scrollback, not a dumbed-down GUI
32
- - **Built-in shortcut keys** โ€” `y` / `n` for approvals, `Tab`, `Ctrl+C`, arrow keys, all one-tap
33
- - **`claude` quick-launch button** โ€” start new sessions, resume, or continue with one tap
34
- - **Pixel office mode** โ€” see all 4 terminals as cute ghost desks
29
+ No VPN. No Tailscale. No port forwarding. Just one command.
30
+
31
+ ---
35
32
 
36
33
  ## Quick Start
37
34
 
@@ -45,13 +42,83 @@ First run opens a browser for Google sign-in. After that, it remembers you.
45
42
 
46
43
  ### 2. On your phone
47
44
 
48
- Open **[ghostterm.pages.dev](https://ghostterm.pages.dev)** โ€” sign in with the same Google account.
45
+ Open **[ghostterm.pages.dev](https://ghostterm.pages.dev)** in any mobile browser. Sign in with the same Google account.
46
+
47
+ **That's it.** Your terminal appears on your phone.
48
+
49
+ ---
50
+
51
+ ## Screenshots
52
+
53
+ <p align="center">
54
+ <img src="https://ghostterm.pages.dev/img/office-busy.jpg" width="220" alt="Ghosts working">
55
+ <img src="https://ghostterm.pages.dev/img/pixel-office.jpg" width="220" alt="Pixel office">
56
+ <img src="https://ghostterm.pages.dev/img/claude-code.jpg" width="220" alt="Claude Code on phone">
57
+ </p>
58
+ <p align="center">
59
+ <em>Pixel ghost office ยท 4 terminal sessions ยท Claude Code on your phone</em>
60
+ </p>
61
+
62
+ ---
63
+
64
+ ## Features
65
+
66
+ ### Terminal
67
+
68
+ - **Real xterm.js terminal** โ€” full ANSI colors, 256-color, scrollback buffer
69
+ - **4 simultaneous sessions** โ€” more than Anthropic's official mobile app
70
+ - **Auto-resize** โ€” terminal adapts to your phone's screen size
71
+ - **Scrollback history** โ€” scroll up to see previous output, page up/down buttons
72
+
73
+ ### Controls
74
+
75
+ - **Quick keys** โ€” one-tap `y`/`n` for Claude Code approvals
76
+ - **D-pad** โ€” arrow keys, Enter, Tab, Shift+Tab
77
+ - **`claude` button** โ€” quick-launch menu: new session, resume, continue, dangerous mode
78
+ - **Ctrl+C** โ€” interrupt running processes
79
+ - **Text input** โ€” full keyboard input with Send button
80
+ - **Copy mode** โ€” select and copy terminal text
81
+
82
+ ### File Transfer
83
+
84
+ - **Screenshot** โ€” capture your phone screen and send it to your PC terminal
85
+ - **File upload** โ€” upload files directly from your phone to your desktop
86
+
87
+ ### Visual
88
+
89
+ - **Pixel office mode** โ€” see all 4 terminals as cute ghost desks in a Halloween-themed pixel office
90
+ - **Ghost cell previews** โ€” live miniature terminal previews in the header
91
+ - **Dark theme** โ€” easy on the eyes, matches terminal aesthetics
92
+
93
+ ### Connection
94
+
95
+ - **Google auto-pairing** โ€” sign in once, auto-reconnects forever
96
+ - **Encrypted relay** โ€” all traffic over WSS (WebSocket Secure)
97
+ - **Zero data stored** โ€” the relay only forwards messages
98
+ - **Auto-reconnect** โ€” handles network drops gracefully
49
99
 
50
- That's it. Your terminal appears on your phone. No VPN. No Tailscale. No port forwarding.
100
+ ---
101
+
102
+ ## How It Works
103
+
104
+ ```
105
+ Phone (browser) โ”€โ”€WSSโ”€โ”€> Relay (Fly.io Tokyo) <โ”€โ”€WSSโ”€โ”€ PC (ghostterm)
106
+ encrypted WebSocket
107
+ auto-pairs by Google account
108
+ zero data stored
109
+ ```
110
+
111
+ 1. `npx ghostterm` starts a companion process on your PC that connects to the relay server
112
+ 2. The companion spawns a real PTY (pseudo-terminal) using `node-pty`
113
+ 3. Your phone connects to the same relay via WebSocket
114
+ 4. The relay matches both sides by Google account and forwards messages
115
+ 5. Terminal output streams to your phone; your input streams to the PTY
116
+
117
+ ---
51
118
 
52
119
  ## Use Cases
53
120
 
54
- ### ๐Ÿ”ฅ The "Dangerous" Workflow
121
+ ### The "Dangerous" Workflow
55
122
 
56
123
  ```bash
57
124
  claude --dangerously-skip-permissions
@@ -59,75 +126,141 @@ claude --dangerously-skip-permissions
59
126
 
60
127
  Let Claude Code run fully autonomous on your PC. Monitor everything from your phone. Intervene when needed. Go make coffee.
61
128
 
62
- ### ๐Ÿ–ฅ๏ธ Multi-Session Power
129
+ ### Multi-Session Power
63
130
 
64
131
  4 ghost cells = 4 terminals. Run Claude Code in one, `git log` in another, `npm test` in a third. Switch between them with a tap.
65
132
 
66
- ### ๐Ÿ“ฑ On-the-Go Approval
133
+ ### On-the-Go Approval
67
134
 
68
135
  Claude Code asking "May I edit server.js?" โ€” tap `y` from the bus. No need to rush back to your desk.
69
136
 
70
- ### ๐Ÿ“ธ Screenshot & Upload
137
+ ---
71
138
 
72
- Take a screenshot of your phone screen and send it to your PC terminal. Upload files directly from your phone to your desktop.
139
+ ## System Requirements
73
140
 
74
- ## Features
141
+ ### PC (Companion)
75
142
 
76
- | Feature | GhostTerm | Anthropic Mobile |
77
- |---|---|---|
78
- | Real CLI terminal | โœ… | โŒ |
79
- | Simultaneous sessions | 4 | 1 |
80
- | `--dangerously-skip-permissions` | โœ… monitor from phone | โŒ |
81
- | Custom shortcut keys | โœ… y/n/Tab/Ctrl+C | โŒ |
82
- | Pixel office view | โœ… | โŒ |
83
- | File upload to PC | โœ… | โŒ |
84
- | No VPN needed | โœ… | N/A |
85
- | Google auto-pairing | โœ… | - |
143
+ - **Node.js** 18 or later
144
+ - **Operating System**: Windows, macOS, or Linux
145
+ - **Platform-specific build tools** (for `node-pty`):
146
+ - **Windows**: Visual Studio Build Tools or `npm install -g windows-build-tools`
147
+ - **macOS**: `xcode-select --install`
148
+ - **Linux**: `apt install build-essential` (Debian/Ubuntu) or equivalent
149
+ - **Google account** for pairing
86
150
 
87
- ## Controls
151
+ ### Phone (Client)
88
152
 
89
- **Top bar**: 4 ghost cells (tap to switch terminals, `+` to create new) ยท `๐Ÿ ` pixel office view ยท `A` font size ยท `โ–ผ` hide controls
153
+ - Any modern browser (iOS Safari 15+, Chrome, Firefox, Edge)
154
+ - Internet connection
155
+
156
+ ---
90
157
 
91
- **Quick keys**: `y` / `n` (approve/deny) ยท `S+Tab` (shift-tab) ยท `/ cmd` (slash commands) ยท `Tab` ยท `โ†Bksp`
158
+ ## Configuration
92
159
 
93
- **Left column**: `โป claude` (launch menu: new / resume / continue / dangerous mode) ยท `Stop` (Ctrl+C) ยท `Close` (kill session)
160
+ ### Environment Variables
94
161
 
95
- **Center**: D-pad (โ†‘โ†“โ†โ†’) + `Enter`
162
+ | Variable | Default | Description |
163
+ |---|---|---|
164
+ | `GHOSTTERM_RELAY` | `wss://ghostterm-relay.fly.dev` | Custom relay server URL |
96
165
 
97
- **Right column**: `Lineโ†ต` (newline) ยท `โฌ‡` (scroll to bottom) ยท `Space` ยท `โ–ฒโ–ผ` (page up/down) ยท `Copy` (select mode)
166
+ ### Custom Relay
98
167
 
99
- **Bottom**: Text input + `Send` ยท `๐Ÿ“ท Shot` (screenshot to PC) ยท `๐Ÿ“ File` (upload to PC)
168
+ If you want to self-host the relay:
169
+
170
+ ```bash
171
+ GHOSTTERM_RELAY=wss://your-relay.example.com npx ghostterm
172
+ ```
173
+
174
+ ---
100
175
 
101
176
  ## Pricing
102
177
 
103
- - **Free**: 1 hour/day, full features
178
+ - **Free**: 1 hour/day, all features included
104
179
  - **Pro**: $5/month or $12/quarter โ€” unlimited access
105
180
 
106
- ## How It Works
181
+ ---
182
+
183
+ ## Troubleshooting
184
+
185
+ ### "Failed to install node-pty" / build errors
186
+
187
+ `node-pty` requires native compilation. Make sure you have build tools installed:
188
+
189
+ ```bash
190
+ # Windows (run as Administrator)
191
+ npm install -g windows-build-tools
192
+
193
+ # macOS
194
+ xcode-select --install
107
195
 
196
+ # Linux (Debian/Ubuntu)
197
+ sudo apt install build-essential python3
108
198
  ```
109
- Phone (browser) โ”€โ”€WSSโ”€โ”€โ–ถ Relay (Fly.io Tokyo) โ—€โ”€โ”€WSSโ”€โ”€ PC (ghostterm)
110
- encrypted WebSocket
111
- auto-pairs by Google account
112
- zero data stored
199
+
200
+ ### "Google sign-in window doesn't open"
201
+
202
+ - Make sure your default browser isn't blocking pop-ups
203
+ - Try running `npx ghostterm` again โ€” it retries automatically
204
+ - If behind a corporate proxy, ensure `accounts.google.com` is accessible
205
+
206
+ ### "Phone can't connect" / "Waiting for companion..."
207
+
208
+ - Ensure the PC companion is still running (`npx ghostterm`)
209
+ - Both devices must use the **same Google account**
210
+ - Check your phone's internet connection
211
+ - Try refreshing the page on your phone
212
+
213
+ ### "Terminal shows garbled text"
214
+
215
+ - Make sure your phone browser supports modern CSS/JS
216
+ - Try switching to a different terminal session (tap a ghost cell)
217
+ - Clear browser cache and reload
218
+
219
+ ### "Session keeps disconnecting"
220
+
221
+ - Check your network stability on both PC and phone
222
+ - The relay auto-reconnects with exponential backoff
223
+ - If using a VPN, try disabling it (GhostTerm doesn't need one)
224
+
225
+ ### node-pty on Apple Silicon (M1/M2/M3)
226
+
227
+ If you see architecture mismatch errors:
228
+
229
+ ```bash
230
+ # Force rebuild for current architecture
231
+ npm rebuild node-pty
113
232
  ```
114
233
 
115
- All traffic is encrypted (WSS). The relay only forwards messages โ€” no terminal data is stored.
234
+ ---
116
235
 
117
- ## Requirements
236
+ ## Uninstall
118
237
 
119
- - **PC**: Node.js 18+
120
- - Windows: may need `npm install -g windows-build-tools`
121
- - macOS: `xcode-select --install`
122
- - Linux: `apt install build-essential`
123
- - **Phone**: Any modern browser (iOS Safari, Chrome, etc.)
238
+ GhostTerm stores a refresh token in `~/.ghostterm/` for auto-login. To fully remove:
124
239
 
125
- ## Environment Variables
240
+ ```bash
241
+ rm -rf ~/.ghostterm
242
+ ```
126
243
 
127
- | Variable | Default | Description |
128
- |---|---|---|
129
- | `GHOSTTERM_RELAY` | `wss://ghostterm-relay.fly.dev` | Custom relay server URL |
244
+ ---
245
+
246
+ ## Security
247
+
248
+ - All communication is encrypted via WSS (WebSocket Secure)
249
+ - Google OAuth 2.0 for authentication โ€” GhostTerm never sees your Google password
250
+ - The relay server is stateless โ€” no terminal data is stored
251
+ - Pair codes expire after 5 minutes
252
+ - Brute-force protection on pair code entry
253
+ - Rate limiting on all WebSocket connections
254
+ - Connection limits per IP address
255
+
256
+ ---
130
257
 
131
258
  ## License
132
259
 
133
260
  MIT
261
+
262
+ ---
263
+
264
+ <p align="center">
265
+ Built with frustration and love by a Claude Code addict.
266
+ </p>
package/bin/ghostterm.js CHANGED
@@ -37,11 +37,23 @@ function loadToken() {
37
37
  if (fs.existsSync(CRED_FILE)) {
38
38
  const cred = JSON.parse(fs.readFileSync(CRED_FILE, 'utf8'));
39
39
  if (cred.id_token) {
40
- const payload = JSON.parse(Buffer.from(cred.id_token.split('.')[1], 'base64').toString());
41
- if (payload.exp * 1000 > Date.now()) {
42
- return cred.id_token;
40
+ // Long token format: base64payload.signature (no dots in payload)
41
+ const parts = cred.id_token.split('.');
42
+ if (parts.length === 2) {
43
+ // Long token โ€” check our own expiry
44
+ try {
45
+ const data = JSON.parse(Buffer.from(parts[0], 'base64').toString());
46
+ if (data.exp > Date.now()) return cred.id_token;
47
+ console.log(' Long token expired, need to re-login');
48
+ } catch {}
49
+ } else if (parts.length === 3) {
50
+ // Google JWT โ€” check Google expiry
51
+ try {
52
+ const payload = JSON.parse(Buffer.from(parts[1], 'base64').toString());
53
+ if (payload.exp * 1000 > Date.now()) return cred.id_token;
54
+ console.log(' Google token expired, need to re-login');
55
+ } catch {}
43
56
  }
44
- console.log(' Token expired, need to re-login');
45
57
  }
46
58
  }
47
59
  } catch {}
@@ -303,7 +315,12 @@ function handleRelayMessage(msg) {
303
315
  case 'auth_ok':
304
316
  console.log(` Authenticated as: ${msg.email}`);
305
317
  console.log(' Phone will auto-connect with same Google account');
306
- // Pre-spawn a standby terminal so first open is instant
318
+ // Save long token if provided (valid 30 days, no more Google expiry issues)
319
+ if (msg.longToken) {
320
+ saveToken(msg.longToken);
321
+ googleToken = msg.longToken;
322
+ console.log(' Long-lived token saved (30 days)');
323
+ }
307
324
  prepareStandby();
308
325
  break;
309
326
 
@@ -392,8 +409,19 @@ function handleRelayMessage(msg) {
392
409
 
393
410
  case 'upload':
394
411
  try {
412
+ const UPLOAD_MAX_SIZE = 5 * 1024 * 1024; // 5MB
413
+ const UPLOAD_EXT_WHITELIST = ['.png', '.jpg', '.jpeg', '.pdf', '.txt', '.md', '.json', '.csv', '.py', '.js', '.ts', '.html', '.css'];
414
+ const ext = (msg.ext || '.png').toLowerCase();
415
+ if (!UPLOAD_EXT_WHITELIST.includes(ext)) {
416
+ sendToRelay({ type: 'upload_error', message: `File type not allowed: ${ext}` });
417
+ break;
418
+ }
395
419
  const buf = Buffer.from(msg.data, 'base64');
396
- const filename = `${Date.now()}-${Math.random().toString(36).slice(2, 6)}${msg.ext || '.png'}`;
420
+ if (buf.length > UPLOAD_MAX_SIZE) {
421
+ sendToRelay({ type: 'upload_error', message: `File too large: ${(buf.length / 1024 / 1024).toFixed(1)}MB (max 5MB)` });
422
+ break;
423
+ }
424
+ const filename = `${Date.now()}-${Math.random().toString(36).slice(2, 6)}${ext}`;
397
425
  const filePath = path.join(UPLOAD_DIR, filename);
398
426
  fs.writeFileSync(filePath, buf);
399
427
  sendToRelay({ type: 'upload_result', path: filePath, filename, size: buf.length });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ghostterm",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "description": "Mobile terminal for Claude Code โ€” control your PC from your phone",
5
5
  "bin": {
6
6
  "ghostterm": "bin/ghostterm.js"