ghostterm 2.2.1 → 2.3.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
@@ -56,14 +56,21 @@ Purpose-built buttons for Claude Code workflows:
56
56
 
57
57
  ## Quick Start
58
58
 
59
+ **Install once:**
59
60
  ```bash
60
- npx ghostterm
61
+ npm install -g ghostterm
61
62
  ```
62
63
 
63
- 1. Run the command above on your PC (requires [Node.js 18+](https://nodejs.org))
64
- 2. First time: browser opens for Google sign-in (one-time, remembered after)
65
- 3. Open **[ghostterm.pages.dev](https://ghostterm.pages.dev)** on your phone
66
- 4. Sign in with the same Google account → **auto-connects, no pairing codes**
64
+ **Then just run:**
65
+ ```bash
66
+ ghostterm
67
+ ```
68
+
69
+ 1. First time: browser opens for Google sign-in (one-time, remembered after)
70
+ 2. Open **[ghostterm.pages.dev](https://ghostterm.pages.dev)** on your phone
71
+ 3. Sign in with the same Google account → **auto-connects, no pairing codes**
72
+
73
+ > Don't want to install? Use `npx ghostterm` to run without installing.
67
74
 
68
75
  ## Security
69
76
 
@@ -11,7 +11,6 @@ process.on('uncaughtException', (err) => {
11
11
  });
12
12
 
13
13
  const WebSocket = require('ws');
14
- const qrcode = require('qrcode-terminal');
15
14
  const { CompanionWebRTC } = require('../lib/webrtc-peer');
16
15
  const { PtyManager } = require('../lib/pty-manager');
17
16
  const { getToken, saveToken } = require('../lib/auth');
@@ -60,9 +59,8 @@ if (helpRequested) {
60
59
  let ws = null;
61
60
  let webrtc = null;
62
61
  let ptyManager = null;
63
- let pairCode = null;
64
62
  let reconnecting = false;
65
- let authToken = null; // Google or long token
63
+ let authToken = null;
66
64
 
67
65
  // --- Main ---
68
66
 
@@ -122,13 +120,25 @@ function connectSignaling() {
122
120
 
123
121
  function handleSignalingMessage(msg) {
124
122
  switch (msg.type) {
125
- case 'pair_code':
126
- pairCode = msg.code;
127
- displayPairCode(msg.code);
123
+ case 'waiting_auth':
124
+ // Server ready, waiting for Google auth
125
+ break;
126
+
127
+ case 'auth_ok':
128
+ console.log(` Authenticated as: ${msg.email}`);
129
+ console.log(`\n Open ${siteUrl} on your phone`);
130
+ console.log(' Sign in with the same Google account to connect.\n');
131
+ if (msg.longToken) {
132
+ saveToken(null, msg.longToken);
133
+ authToken = msg.longToken;
134
+ }
135
+ if (msg.autoPaired) {
136
+ console.log(' Mobile already connected! Establishing P2P...');
137
+ }
128
138
  break;
129
139
 
130
140
  case 'mobile_connected':
131
- console.log('\n Mobile device paired! Establishing P2P connection...');
141
+ console.log('\n Mobile device connected! Establishing P2P...');
132
142
  initiateWebRTC();
133
143
  break;
134
144
 
@@ -140,31 +150,14 @@ function handleSignalingMessage(msg) {
140
150
  break;
141
151
 
142
152
  case 'mobile_disconnected':
143
- // Ignore if P2P is active — mobile closed signaling WS after P2P established (expected)
144
- if (webrtc && webrtc.isActive) {
145
- // P2P still alive, signaling disconnect is normal
146
- break;
147
- }
153
+ if (webrtc && webrtc.isActive) break;
148
154
  console.log('\n Mobile disconnected.');
149
155
  cleanupWebRTC();
150
156
  break;
151
157
 
152
- case 'pair_expired':
153
- console.log('\n Pair code expired. Reconnecting for a new code...');
154
- ws.close();
155
- break;
156
-
157
- case 'auth_ok':
158
- console.log(` Authenticated as: ${msg.email}`);
159
- // Save long token for future sessions
160
- if (msg.longToken) {
161
- saveToken(null, msg.longToken);
162
- authToken = msg.longToken;
163
- }
164
- break;
165
-
166
158
  case 'auth_error':
167
159
  console.error(` Auth failed: ${msg.message}`);
160
+ console.error(' Try deleting ~/.ghostterm/credentials.json and restart.');
168
161
  break;
169
162
 
170
163
  case 'error':
@@ -173,23 +166,6 @@ function handleSignalingMessage(msg) {
173
166
  }
174
167
  }
175
168
 
176
- function displayPairCode(code) {
177
- const mobileUrl = `${siteUrl}?code=${code}`;
178
-
179
- console.log('\n +--------------------------+');
180
- console.log(` | Pair Code: ${code} |`);
181
- console.log(' +--------------------------+');
182
- console.log(`\n Or scan QR code with your phone:\n`);
183
-
184
- qrcode.generate(mobileUrl, { small: true }, (qr) => {
185
- // Indent each line
186
- const lines = qr.split('\n').map(l => ' ' + l).join('\n');
187
- console.log(lines);
188
- console.log(`\n Mobile URL: ${mobileUrl}`);
189
- console.log('\n Waiting for phone to connect...\n');
190
- });
191
- }
192
-
193
169
  function initiateWebRTC() {
194
170
  cleanupWebRTC();
195
171
 
@@ -269,11 +269,15 @@ class PtyManager extends EventEmitter {
269
269
  }
270
270
 
271
271
  case 'file-upload': {
272
- // Handle file upload: write base64 content to temp file
273
272
  this._handleFileUpload(msg, responses);
274
273
  break;
275
274
  }
276
275
 
276
+ case 'file-chunk': {
277
+ this._handleFileChunk(msg, responses);
278
+ break;
279
+ }
280
+
277
281
  default:
278
282
  break;
279
283
  }
@@ -315,6 +319,30 @@ class PtyManager extends EventEmitter {
315
319
  }
316
320
  }
317
321
 
322
+ _handleFileChunk(msg, responses) {
323
+ if (!this._fileChunks) this._fileChunks = {};
324
+ const key = msg.filename;
325
+ if (!this._fileChunks[key]) {
326
+ this._fileChunks[key] = { chunks: [], total: msg.total, ext: msg.ext, size: msg.size };
327
+ }
328
+ const entry = this._fileChunks[key];
329
+ entry.chunks[msg.index] = msg.chunk;
330
+
331
+ // Check if all chunks received
332
+ const received = entry.chunks.filter(Boolean).length;
333
+ if (received === entry.total) {
334
+ // Reassemble
335
+ const fullBase64 = entry.chunks.join('');
336
+ delete this._fileChunks[key];
337
+ this._handleFileUpload({
338
+ data: fullBase64,
339
+ filename: msg.filename,
340
+ ext: entry.ext,
341
+ size: entry.size,
342
+ }, responses);
343
+ }
344
+ }
345
+
318
346
  _sessionList() {
319
347
  const list = [];
320
348
  for (const [id, session] of this.sessions) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ghostterm",
3
- "version": "2.2.1",
3
+ "version": "2.3.0",
4
4
  "description": "Control your PC terminal from your phone — direct P2P, no server in between",
5
5
  "bin": {
6
6
  "ghostterm": "bin/ghostterm-p2p.js"
@@ -14,8 +14,7 @@
14
14
  "dependencies": {
15
15
  "node-datachannel": "^0.12.0",
16
16
  "node-pty": "^1.0.0",
17
- "ws": "^8.0.0",
18
- "qrcode-terminal": "^0.12.0"
17
+ "ws": "^8.0.0"
19
18
  },
20
19
  "engines": {
21
20
  "node": ">=18.0.0"