ghostterm 1.1.3 → 1.2.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/CHANGELOG.md +5 -1
- package/README.md +13 -4
- package/bin/ghostterm.js +118 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 1.
|
|
3
|
+
## 1.2.0 (2026-03-17)
|
|
4
4
|
|
|
5
5
|
### Features
|
|
6
|
+
- **Background mode by default** — `npx ghostterm` now runs in the background automatically, no terminal window
|
|
7
|
+
- **CLI commands** — `npx ghostterm stop`, `npx ghostterm status`, `npx ghostterm logs`
|
|
8
|
+
- **First-run login** — opens browser for Google sign-in in foreground, then auto-restarts as daemon
|
|
9
|
+
- **Duplicate detection** — prevents starting multiple instances
|
|
6
10
|
- **12345 numpad popup** — quick number selection for CLI surveys/prompts (raw keypress without Enter)
|
|
7
11
|
- **Screen button** — renamed from "Shot" for clarity
|
|
8
12
|
- **Paste button shows filename** — after uploading, displays shortened filename instead of generic "Paste"
|
package/README.md
CHANGED
|
@@ -38,13 +38,22 @@ No VPN. No Tailscale. No port forwarding. Just one command.
|
|
|
38
38
|
npx ghostterm
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
-
First run opens a browser for Google sign-in. After that, it
|
|
41
|
+
First run opens a browser for Google sign-in. After that, it runs in the background automatically — no terminal window.
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
```
|
|
44
|
+
$ npx ghostterm
|
|
45
|
+
✅ GhostTerm running in background (PID: 12345)
|
|
46
|
+
📱 Open: ghostterm.pages.dev
|
|
47
|
+
🛑 Stop: npx ghostterm stop
|
|
48
|
+
📋 Logs: npx ghostterm logs
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Other commands:
|
|
44
52
|
|
|
45
53
|
```bash
|
|
46
|
-
|
|
47
|
-
|
|
54
|
+
npx ghostterm status # check if running
|
|
55
|
+
npx ghostterm stop # stop the background process
|
|
56
|
+
npx ghostterm logs # show recent logs
|
|
48
57
|
```
|
|
49
58
|
|
|
50
59
|
### 2. On your phone
|
package/bin/ghostterm.js
CHANGED
|
@@ -433,8 +433,106 @@ function handleRelayMessage(msg) {
|
|
|
433
433
|
}
|
|
434
434
|
}
|
|
435
435
|
|
|
436
|
+
// ==================== Background Mode ====================
|
|
437
|
+
const PID_FILE = path.join(CRED_DIR, 'ghostterm.pid');
|
|
438
|
+
const LOG_FILE = path.join(CRED_DIR, 'ghostterm.log');
|
|
439
|
+
|
|
440
|
+
function isRunning(pid) {
|
|
441
|
+
try { process.kill(pid, 0); return true; } catch { return false; }
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
function handleSubcommand() {
|
|
445
|
+
const arg = process.argv[2];
|
|
446
|
+
|
|
447
|
+
if (arg === 'stop') {
|
|
448
|
+
if (fs.existsSync(PID_FILE)) {
|
|
449
|
+
const pid = parseInt(fs.readFileSync(PID_FILE, 'utf8'));
|
|
450
|
+
if (isRunning(pid)) {
|
|
451
|
+
process.kill(pid);
|
|
452
|
+
fs.unlinkSync(PID_FILE);
|
|
453
|
+
console.log(` GhostTerm stopped (PID: ${pid})`);
|
|
454
|
+
} else {
|
|
455
|
+
fs.unlinkSync(PID_FILE);
|
|
456
|
+
console.log(' GhostTerm was not running');
|
|
457
|
+
}
|
|
458
|
+
} else {
|
|
459
|
+
console.log(' GhostTerm is not running');
|
|
460
|
+
}
|
|
461
|
+
process.exit(0);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
if (arg === 'status') {
|
|
465
|
+
if (fs.existsSync(PID_FILE)) {
|
|
466
|
+
const pid = parseInt(fs.readFileSync(PID_FILE, 'utf8'));
|
|
467
|
+
if (isRunning(pid)) {
|
|
468
|
+
console.log(` GhostTerm is running (PID: ${pid})`);
|
|
469
|
+
} else {
|
|
470
|
+
fs.unlinkSync(PID_FILE);
|
|
471
|
+
console.log(' GhostTerm is not running');
|
|
472
|
+
}
|
|
473
|
+
} else {
|
|
474
|
+
console.log(' GhostTerm is not running');
|
|
475
|
+
}
|
|
476
|
+
process.exit(0);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
if (arg === 'logs') {
|
|
480
|
+
if (fs.existsSync(LOG_FILE)) {
|
|
481
|
+
const lines = fs.readFileSync(LOG_FILE, 'utf8').split('\n').slice(-50);
|
|
482
|
+
console.log(lines.join('\n'));
|
|
483
|
+
} else {
|
|
484
|
+
console.log(' No logs found');
|
|
485
|
+
}
|
|
486
|
+
process.exit(0);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// No subcommand = start in background (unless already daemonized via --daemon)
|
|
490
|
+
if (arg !== '--daemon') {
|
|
491
|
+
// Check if already running
|
|
492
|
+
if (fs.existsSync(PID_FILE)) {
|
|
493
|
+
const pid = parseInt(fs.readFileSync(PID_FILE, 'utf8'));
|
|
494
|
+
if (isRunning(pid)) {
|
|
495
|
+
console.log(` GhostTerm is already running (PID: ${pid})`);
|
|
496
|
+
console.log(' Stop: npx ghostterm stop');
|
|
497
|
+
process.exit(0);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// First-time login needs foreground (browser opens)
|
|
502
|
+
const needsLogin = !fs.existsSync(CRED_FILE);
|
|
503
|
+
if (needsLogin) {
|
|
504
|
+
// Run in foreground for first login, then restart as daemon
|
|
505
|
+
return 'foreground-first-login';
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// Spawn self as daemon
|
|
509
|
+
const { spawn } = require('child_process');
|
|
510
|
+
const out = fs.openSync(LOG_FILE, 'a');
|
|
511
|
+
const child = spawn(process.execPath, [__filename, '--daemon'], {
|
|
512
|
+
detached: true,
|
|
513
|
+
stdio: ['ignore', out, out],
|
|
514
|
+
env: { ...process.env },
|
|
515
|
+
});
|
|
516
|
+
child.unref();
|
|
517
|
+
fs.writeFileSync(PID_FILE, String(child.pid));
|
|
518
|
+
console.log('');
|
|
519
|
+
console.log(` ✅ GhostTerm running in background (PID: ${child.pid})`);
|
|
520
|
+
console.log(' 📱 Open: ghostterm.pages.dev');
|
|
521
|
+
console.log(' 🛑 Stop: npx ghostterm stop');
|
|
522
|
+
console.log(' 📋 Logs: npx ghostterm logs');
|
|
523
|
+
console.log('');
|
|
524
|
+
process.exit(0);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// --daemon: write PID file and continue to main()
|
|
528
|
+
fs.writeFileSync(PID_FILE, String(process.pid));
|
|
529
|
+
return 'daemon';
|
|
530
|
+
}
|
|
531
|
+
|
|
436
532
|
// ==================== Main ====================
|
|
437
533
|
async function main() {
|
|
534
|
+
const mode = handleSubcommand();
|
|
535
|
+
|
|
438
536
|
console.log('');
|
|
439
537
|
console.log(' ╔═══════════════════════════════════╗');
|
|
440
538
|
console.log(' ║ GhostTerm Companion ║');
|
|
@@ -449,6 +547,26 @@ async function main() {
|
|
|
449
547
|
console.log(' Will use pairing code instead');
|
|
450
548
|
}
|
|
451
549
|
|
|
550
|
+
// First login done in foreground — now restart as daemon
|
|
551
|
+
if (mode === 'foreground-first-login' && googleToken) {
|
|
552
|
+
console.log('');
|
|
553
|
+
console.log(' Login successful! Restarting in background...');
|
|
554
|
+
const { spawn } = require('child_process');
|
|
555
|
+
const out = fs.openSync(LOG_FILE, 'a');
|
|
556
|
+
const child = spawn(process.execPath, [__filename, '--daemon'], {
|
|
557
|
+
detached: true,
|
|
558
|
+
stdio: ['ignore', out, out],
|
|
559
|
+
env: { ...process.env },
|
|
560
|
+
});
|
|
561
|
+
child.unref();
|
|
562
|
+
fs.writeFileSync(PID_FILE, String(child.pid));
|
|
563
|
+
console.log(` ✅ GhostTerm running in background (PID: ${child.pid})`);
|
|
564
|
+
console.log(' 📱 Open: ghostterm.pages.dev');
|
|
565
|
+
console.log(' 🛑 Stop: npx ghostterm stop');
|
|
566
|
+
console.log('');
|
|
567
|
+
process.exit(0);
|
|
568
|
+
}
|
|
569
|
+
|
|
452
570
|
connectToRelay();
|
|
453
571
|
}
|
|
454
572
|
|