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 CHANGED
@@ -1,8 +1,12 @@
1
1
  # Changelog
2
2
 
3
- ## 1.1.3 (2026-03-17)
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 remembers you.
41
+ First run opens a browser for Google sign-in. After that, it runs in the background automatically — no terminal window.
42
42
 
43
- **Run in background (no terminal window):**
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
- pm2 start ghostterm.js --name ghostterm
47
- pm2 save
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
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ghostterm",
3
- "version": "1.1.3",
3
+ "version": "1.2.0",
4
4
  "description": "Mobile terminal for Claude Code — control your PC from your phone",
5
5
  "bin": {
6
6
  "ghostterm": "bin/ghostterm.js"