cli-tunnel 1.2.0-beta.4 → 1.2.0-beta.6

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 (2) hide show
  1. package/dist/index.js +112 -48
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -475,42 +475,36 @@ async function main() {
475
475
  }
476
476
  }
477
477
  if (devtunnelInstalled) {
478
+ // Check if logged in before attempting tunnel creation
478
479
  try {
479
- // Check if logged in before attempting tunnel creation
480
- try {
481
- const userInfo = execFileSync('devtunnel', ['user', 'show'], { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] });
482
- if (userInfo.includes('not logged in') || userInfo.includes('No user') || userInfo.includes('Anonymous')) {
483
- throw new Error('not logged in');
484
- }
480
+ const userInfo = execFileSync('devtunnel', ['user', 'show'], { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] });
481
+ if (userInfo.includes('not logged in') || userInfo.includes('No user') || userInfo.includes('Anonymous')) {
482
+ throw new Error('not logged in');
485
483
  }
486
- catch {
487
- console.log(`\n ${YELLOW}⚠ devtunnel not authenticated.${RESET}\n`);
488
- const loginAnswer = await askUser(` Would you like to log in now? [Y/n] `);
489
- if (loginAnswer === '' || loginAnswer === 'y' || loginAnswer === 'yes') {
490
- try {
491
- const loginProc = spawn('devtunnel', ['user', 'login'], { stdio: 'inherit' });
492
- await new Promise((resolve, reject) => {
493
- loginProc.on('close', (code) => code === 0 ? resolve() : reject(new Error(`Login exited with code ${code}`)));
494
- loginProc.on('error', reject);
495
- });
496
- console.log(`\n ${GREEN}✓${RESET} Logged in successfully!\n`);
497
- }
498
- catch {
499
- console.log(`\n ${YELLOW}⚠${RESET} Login failed. Run manually: ${GREEN}devtunnel user login${RESET}\n`);
500
- console.log(` ${DIM}Continuing without tunnel (local only)...${RESET}\n`);
501
- devtunnelInstalled = false;
502
- }
484
+ }
485
+ catch {
486
+ console.log(`\n ${YELLOW}⚠ devtunnel not authenticated.${RESET}\n`);
487
+ const loginAnswer = await askUser(` Would you like to log in now? [Y/n] `);
488
+ if (loginAnswer === '' || loginAnswer === 'y' || loginAnswer === 'yes') {
489
+ try {
490
+ const loginProc = spawn('devtunnel', ['user', 'login'], { stdio: 'inherit' });
491
+ await new Promise((resolve, reject) => {
492
+ loginProc.on('close', (code) => code === 0 ? resolve() : reject(new Error(`Login exited with code ${code}`)));
493
+ loginProc.on('error', reject);
494
+ });
495
+ console.log(`\n ${GREEN}✓${RESET} Logged in successfully!\n`);
503
496
  }
504
- else {
505
- console.log(`\n ${DIM}Run this once to log in: ${GREEN}devtunnel user login${RESET}`);
497
+ catch {
498
+ console.log(`\n ${YELLOW}⚠${RESET} Login failed. Run manually: ${GREEN}devtunnel user login${RESET}\n`);
506
499
  console.log(` ${DIM}Continuing without tunnel (local only)...${RESET}\n`);
507
500
  devtunnelInstalled = false;
508
501
  }
509
502
  }
510
- }
511
- catch (err) {
512
- console.log(` ${YELLOW}⚠${RESET} Tunnel failed: ${err.message}\n`);
513
- devtunnelInstalled = false;
503
+ else {
504
+ console.log(`\n ${DIM}Run this once to log in: ${GREEN}devtunnel user login${RESET}`);
505
+ console.log(` ${DIM}Continuing without tunnel (local only)...${RESET}\n`);
506
+ devtunnelInstalled = false;
507
+ }
514
508
  }
515
509
  }
516
510
  if (devtunnelInstalled) {
@@ -553,7 +547,28 @@ async function main() {
553
547
  catch { } });
554
548
  }
555
549
  catch (err) {
556
- console.log(` ${YELLOW}⚠${RESET} Tunnel failed: ${err.message}\n`);
550
+ const errMsg = err.message || '';
551
+ // Detect auth failure at create time (expired token, anonymous, etc.)
552
+ if (errMsg.includes('Anonymous') || errMsg.includes('Unauthorized') || errMsg.includes('not permitted')) {
553
+ console.log(`\n ${YELLOW}⚠ devtunnel session expired or not authenticated.${RESET}\n`);
554
+ const loginAnswer = await askUser(` Would you like to log in now? [Y/n] `);
555
+ if (loginAnswer === '' || loginAnswer === 'y' || loginAnswer === 'yes') {
556
+ try {
557
+ const loginProc = spawn('devtunnel', ['user', 'login'], { stdio: 'inherit' });
558
+ await new Promise((resolve, reject) => {
559
+ loginProc.on('close', (code) => code === 0 ? resolve() : reject(new Error(`Login exited with code ${code}`)));
560
+ loginProc.on('error', reject);
561
+ });
562
+ console.log(`\n ${GREEN}✓${RESET} Logged in! Please run cli-tunnel again to create the tunnel.\n`);
563
+ }
564
+ catch {
565
+ console.log(`\n ${YELLOW}⚠${RESET} Login failed. Run manually: ${GREEN}devtunnel user login${RESET}\n`);
566
+ }
567
+ }
568
+ }
569
+ else {
570
+ console.log(` ${YELLOW}⚠${RESET} Tunnel failed: ${errMsg}\n`);
571
+ }
557
572
  }
558
573
  } // end if (devtunnelInstalled)
559
574
  }
@@ -617,44 +632,93 @@ async function main() {
617
632
  cols, rows, cwd,
618
633
  env: safeEnv,
619
634
  });
620
- // Detect CSPRNG crash (Node.js 23 + node-pty issue) and retry via cmd.exe wrapper
635
+ // Detect CSPRNG crash (Node.js 23 + node-pty issue) and retry with fallbacks
621
636
  let ptyExitedEarly = false;
622
637
  const earlyExitCheck = new Promise((resolve) => {
623
638
  ptyProcess.onExit(({ exitCode }) => {
624
- if (exitCode === 134 || exitCode === 3221226505) { // 134 = SIGABRT, 3221226505 = STATUS_BREAKPOINT
639
+ if (exitCode === 134 || exitCode === 3221226505) {
625
640
  ptyExitedEarly = true;
626
641
  resolve();
627
642
  }
628
643
  });
629
- setTimeout(resolve, 2000); // Wait 2s — if still running, it's fine
644
+ setTimeout(resolve, 2000);
630
645
  });
631
646
  await earlyExitCheck;
632
647
  if (ptyExitedEarly && process.platform === 'win32') {
633
- console.log(` ${YELLOW}⚠${RESET} CSPRNG crash detected (Node.js + PTY issue), retrying via cmd.exe wrapper...\n`);
634
- // Spawn through cmd.exe /c this adds a shell layer that avoids the crash
635
- const cmdLine = [resolvedCmd, ...commandArgs].map(a => a.includes(' ') ? `"${a}"` : a).join(' ');
636
- ptyProcess = nodePty.spawn('cmd.exe', ['/c', cmdLine], {
648
+ // Retry 1: Use full environment CSPRNG may need env vars we filtered out
649
+ console.log(` ${YELLOW}⚠${RESET} Process crashed, retrying with full environment...\n`);
650
+ // Blocklist approach: pass everything EXCEPT known dangerous vars
651
+ const DANGEROUS_VARS = new Set(['NODE_OPTIONS', 'NODE_REPL_HISTORY', 'NODE_EXTRA_CA_CERTS',
652
+ 'NODE_PATH', 'NODE_REDIRECT_WARNINGS', 'NODE_PENDING_DEPRECATION',
653
+ 'UV_THREADPOOL_SIZE', 'LD_PRELOAD', 'DYLD_INSERT_LIBRARIES']);
654
+ const fullSafeEnv = {};
655
+ for (const [k, v] of Object.entries(process.env)) {
656
+ if (!DANGEROUS_VARS.has(k) && v !== undefined)
657
+ fullSafeEnv[k] = v;
658
+ }
659
+ ptyProcess = nodePty.spawn(resolvedCmd, commandArgs, {
637
660
  name: 'xterm-256color',
638
661
  cols, rows, cwd,
639
- env: safeEnv,
662
+ env: fullSafeEnv,
640
663
  });
641
- // Check if cmd.exe wrapper also fails
642
- let retryFailed = false;
643
- const retryCheck = new Promise((resolve) => {
664
+ let retry1Failed = false;
665
+ const retry1Check = new Promise((resolve) => {
644
666
  ptyProcess.onExit(({ exitCode }) => {
645
667
  if (exitCode === 134 || exitCode === 3221226505) {
646
- retryFailed = true;
668
+ retry1Failed = true;
647
669
  resolve();
648
670
  }
649
671
  });
650
672
  setTimeout(resolve, 2000);
651
673
  });
652
- await retryCheck;
653
- if (retryFailed) {
654
- const nodeVer = process.version;
655
- console.log(` ${YELLOW}⚠${RESET} The command crashed due to a known Node.js ${nodeVer} + PTY compatibility issue.`);
656
- console.log(` ${BOLD}Fix:${RESET} Install Node.js 22 LTS: ${GREEN}nvm install 22${RESET} or ${GREEN}winget install OpenJS.NodeJS.LTS${RESET}\n`);
657
- process.exit(1);
674
+ await retry1Check;
675
+ if (retry1Failed) {
676
+ // Retry 2: cmd.exe wrapper with full env
677
+ console.log(` ${YELLOW}⚠${RESET} Still crashing, retrying via cmd.exe wrapper...\n`);
678
+ const cmdLine = [resolvedCmd, ...commandArgs].map(a => a.includes(' ') ? `"${a}"` : a).join(' ');
679
+ ptyProcess = nodePty.spawn('cmd.exe', ['/c', cmdLine], {
680
+ name: 'xterm-256color',
681
+ cols, rows, cwd,
682
+ env: fullSafeEnv,
683
+ });
684
+ let retry2Failed = false;
685
+ const retry2Check = new Promise((resolve) => {
686
+ ptyProcess.onExit(({ exitCode }) => {
687
+ if (exitCode === 134 || exitCode === 3221226505) {
688
+ retry2Failed = true;
689
+ resolve();
690
+ }
691
+ });
692
+ setTimeout(resolve, 2000);
693
+ });
694
+ await retry2Check;
695
+ if (retry2Failed) {
696
+ // Retry 3: useConpty: false with full env
697
+ console.log(` ${YELLOW}⚠${RESET} Still crashing, retrying with legacy PTY backend...\n`);
698
+ ptyProcess = nodePty.spawn(resolvedCmd, commandArgs, {
699
+ name: 'xterm-256color',
700
+ cols, rows, cwd,
701
+ env: fullSafeEnv,
702
+ useConpty: false,
703
+ });
704
+ let retry3Failed = false;
705
+ const retry3Check = new Promise((resolve) => {
706
+ ptyProcess.onExit(({ exitCode }) => {
707
+ if (exitCode === 134 || exitCode === 3221226505) {
708
+ retry3Failed = true;
709
+ resolve();
710
+ }
711
+ });
712
+ setTimeout(resolve, 2000);
713
+ });
714
+ await retry3Check;
715
+ if (retry3Failed) {
716
+ const nodeVer = process.version;
717
+ console.log(` ${YELLOW}⚠${RESET} The command crashed due to a known Node.js ${nodeVer} + PTY compatibility issue.`);
718
+ console.log(` ${BOLD}Fix:${RESET} Install Node.js 22 LTS: ${GREEN}nvm install 22${RESET} or ${GREEN}winget install OpenJS.NodeJS.LTS${RESET}\n`);
719
+ process.exit(1);
720
+ }
721
+ }
658
722
  }
659
723
  }
660
724
  ptyProcess.onData((data) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cli-tunnel",
3
- "version": "1.2.0-beta.4",
3
+ "version": "1.2.0-beta.6",
4
4
  "description": "Tunnel any CLI app to your phone — PTY + devtunnel + xterm.js",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",