ghostterm 1.2.0 → 1.2.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.2.2 (2026-03-17)
4
+
5
+ ### Bug Fixes
6
+ - **Windows fully hidden** — use VBS launcher instead of `windowsHide` (which still showed in taskbar)
7
+
8
+ ## 1.2.1 (2026-03-17)
9
+
10
+ ### Bug Fixes
11
+ - **Windows background mode** — fixed node-pty crash in detached mode
12
+ - **Supervisor crash loop** — fixed `--supervisor` arg being caught by "already running" check
13
+
3
14
  ## 1.2.0 (2026-03-17)
4
15
 
5
16
  ### Features
package/bin/ghostterm.js CHANGED
@@ -486,8 +486,8 @@ function handleSubcommand() {
486
486
  process.exit(0);
487
487
  }
488
488
 
489
- // No subcommand = start in background (unless already daemonized via --daemon)
490
- if (arg !== '--daemon') {
489
+ // No subcommand = start in background (unless already daemonized via --daemon or --supervisor)
490
+ if (arg !== '--daemon' && arg !== '--supervisor') {
491
491
  // Check if already running
492
492
  if (fs.existsSync(PID_FILE)) {
493
493
  const pid = parseInt(fs.readFileSync(PID_FILE, 'utf8'));
@@ -505,18 +505,42 @@ function handleSubcommand() {
505
505
  return 'foreground-first-login';
506
506
  }
507
507
 
508
- // Spawn self as daemon
509
- const { spawn } = require('child_process');
508
+ // Spawn self as supervisor in background
509
+ const { spawn, execSync } = require('child_process');
510
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));
511
+
512
+ if (os.platform() === 'win32') {
513
+ // Windows: use VBS to launch completely hidden (no taskbar, no window)
514
+ // node-pty needs a console; VBS ws.Run with 0 = hidden window
515
+ const vbsFile = path.join(CRED_DIR, 'launcher.vbs');
516
+ const cmd = `"${process.execPath}" "${__filename}" --supervisor`;
517
+ fs.writeFileSync(vbsFile,
518
+ `Set ws = CreateObject("WScript.Shell")\n` +
519
+ `ws.Run "${cmd.replace(/"/g, '""')}", 0, False\n`
520
+ );
521
+ require('child_process').execSync(`cscript //nologo "${vbsFile}"`, { stdio: 'ignore' });
522
+ // Wait for supervisor to write PID file
523
+ const waitUntil = Date.now() + 10000;
524
+ while (Date.now() < waitUntil) {
525
+ if (fs.existsSync(PID_FILE)) {
526
+ const pid = fs.readFileSync(PID_FILE, 'utf8').trim();
527
+ if (pid && isRunning(parseInt(pid))) break;
528
+ }
529
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 500);
530
+ }
531
+ } else {
532
+ // macOS/Linux: detached works fine
533
+ const child = spawn(process.execPath, [__filename, '--supervisor'], {
534
+ detached: true,
535
+ stdio: ['ignore', out, out],
536
+ env: { ...process.env },
537
+ });
538
+ child.unref();
539
+ fs.writeFileSync(PID_FILE, String(child.pid));
540
+ }
541
+ const pid = fs.existsSync(PID_FILE) ? fs.readFileSync(PID_FILE, 'utf8').trim() : '?';
518
542
  console.log('');
519
- console.log(` ✅ GhostTerm running in background (PID: ${child.pid})`);
543
+ console.log(` ✅ GhostTerm running in background (PID: ${pid})`);
520
544
  console.log(' 📱 Open: ghostterm.pages.dev');
521
545
  console.log(' 🛑 Stop: npx ghostterm stop');
522
546
  console.log(' 📋 Logs: npx ghostterm logs');
@@ -524,8 +548,35 @@ function handleSubcommand() {
524
548
  process.exit(0);
525
549
  }
526
550
 
551
+ // --supervisor: watchdog that restarts worker on crash
552
+ if (arg === '--supervisor') {
553
+ fs.writeFileSync(PID_FILE, String(process.pid));
554
+ const { spawn } = require('child_process');
555
+ let restartCount = 0;
556
+
557
+ function spawnWorker() {
558
+ const logFd = fs.openSync(LOG_FILE, 'a');
559
+ const worker = spawn(process.execPath, [__filename, '--daemon'], {
560
+ stdio: ['ignore', logFd, logFd],
561
+ env: { ...process.env },
562
+ });
563
+ worker.on('exit', (code) => {
564
+ if (code === 0) {
565
+ // Clean exit (e.g. from stop command), don't restart
566
+ process.exit(0);
567
+ }
568
+ restartCount++;
569
+ const delay = Math.min(restartCount * 3000, 30000); // 3s, 6s, 9s... max 30s
570
+ console.log(` [supervisor] Worker exited (code ${code}), restarting in ${delay / 1000}s... (restart #${restartCount})`);
571
+ setTimeout(spawnWorker, delay);
572
+ });
573
+ }
574
+
575
+ spawnWorker();
576
+ return;
577
+ }
578
+
527
579
  // --daemon: write PID file and continue to main()
528
- fs.writeFileSync(PID_FILE, String(process.pid));
529
580
  return 'daemon';
530
581
  }
531
582
 
@@ -547,23 +598,13 @@ async function main() {
547
598
  console.log(' Will use pairing code instead');
548
599
  }
549
600
 
550
- // First login done in foreground — now restart as daemon
601
+ // First login done in foreground — now restart in background
551
602
  if (mode === 'foreground-first-login' && googleToken) {
552
603
  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('');
604
+ console.log(' Login successful! Starting in background...');
605
+ // Re-run ourselves which will take the normal background start path
606
+ const { execSync } = require('child_process');
607
+ execSync(`"${process.execPath}" "${__filename}"`, { stdio: 'inherit' });
567
608
  process.exit(0);
568
609
  }
569
610
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ghostterm",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "description": "Mobile terminal for Claude Code — control your PC from your phone",
5
5
  "bin": {
6
6
  "ghostterm": "bin/ghostterm.js"