termbeam 1.14.0 → 1.14.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "termbeam",
3
- "version": "1.14.0",
3
+ "version": "1.14.1",
4
4
  "description": "Beam your terminal to any device — mobile-optimized web terminal with multi-session support",
5
5
  "main": "src/server/index.js",
6
6
  "bin": {
@@ -1,8 +1,27 @@
1
1
  const crypto = require('crypto');
2
2
  const path = require('path');
3
3
  const { exec } = require('child_process');
4
- const pty = require('node-pty');
5
4
  const log = require('../utils/logger');
5
+
6
+ let pty;
7
+ try {
8
+ pty = require('node-pty');
9
+ } catch (err) {
10
+ const isLinux = process.platform === 'linux';
11
+ console.error('\n ❌ Failed to load node-pty — terminal sessions require this native module.\n');
12
+ console.error(` Error: ${err.message.split('\n')[0]}\n`);
13
+ if (isLinux) {
14
+ console.error(' On Linux (including WSL/devbox), you need build tools to compile node-pty:');
15
+ console.error(' Ubuntu/Debian: sudo apt-get install -y build-essential python3');
16
+ console.error(' Fedora/RHEL: sudo dnf groupinstall "Development Tools"');
17
+ console.error(' Alpine: apk add build-base python3\n');
18
+ console.error(' Then rebuild: npm rebuild node-pty');
19
+ console.error(' (or reinstall: npm i -g termbeam)\n');
20
+ } else {
21
+ console.error(' Try rebuilding: npm rebuild node-pty\n');
22
+ }
23
+ process.exit(1);
24
+ }
6
25
  const { getGitInfo } = require('../utils/git');
7
26
 
8
27
  // Cache git info per session to avoid blocking the event loop on every list() call.
@@ -14,6 +14,59 @@ let devtunnelCmd = 'devtunnel';
14
14
 
15
15
  const SAFE_ID_RE = /^[a-zA-Z0-9._-]+$/;
16
16
 
17
+ const DEVICE_CODE_INITIAL_TIMEOUT = 15000;
18
+ const DEVICE_CODE_AUTH_TIMEOUT = 120000;
19
+
20
+ function deviceCodeLogin(cmd) {
21
+ return new Promise((resolve, reject) => {
22
+ const proc = spawn(cmd, ['user', 'login', '-d'], {
23
+ stdio: ['inherit', 'pipe', 'pipe'],
24
+ });
25
+
26
+ let gotOutput = false;
27
+
28
+ const initialTimer = setTimeout(() => {
29
+ if (!gotOutput) {
30
+ proc.kill();
31
+ reject(
32
+ new Error(
33
+ 'Device code flow produced no output — devtunnel may not work in this environment.\n' +
34
+ ' Try logging in manually first: devtunnel user login',
35
+ ),
36
+ );
37
+ }
38
+ }, DEVICE_CODE_INITIAL_TIMEOUT);
39
+
40
+ const overallTimer = setTimeout(() => {
41
+ proc.kill();
42
+ reject(new Error('Device code login timed out — authentication was not completed in time.'));
43
+ }, DEVICE_CODE_AUTH_TIMEOUT);
44
+
45
+ proc.stdout.on('data', (data) => {
46
+ gotOutput = true;
47
+ process.stdout.write(data);
48
+ });
49
+
50
+ proc.stderr.on('data', (data) => {
51
+ gotOutput = true;
52
+ process.stderr.write(data);
53
+ });
54
+
55
+ proc.on('close', (code) => {
56
+ clearTimeout(initialTimer);
57
+ clearTimeout(overallTimer);
58
+ if (code === 0) resolve();
59
+ else reject(new Error(`Device code login exited with code ${code}`));
60
+ });
61
+
62
+ proc.on('error', (err) => {
63
+ clearTimeout(initialTimer);
64
+ clearTimeout(overallTimer);
65
+ reject(err);
66
+ });
67
+ });
68
+ }
69
+
17
70
  function findDevtunnel() {
18
71
  // Try devtunnel directly
19
72
  try {
@@ -123,7 +176,7 @@ async function startTunnel(port, options = {}) {
123
176
  log.info('Browser login failed or unavailable, falling back to device code flow...');
124
177
  log.info('A code will be displayed — open the URL on any device to authenticate.');
125
178
  try {
126
- execFileSync(devtunnelCmd, ['user', 'login', '-d'], { stdio: 'inherit' });
179
+ await deviceCodeLogin(devtunnelCmd);
127
180
  } catch (_loginErr) {
128
181
  log.error('');
129
182
  log.error(' DevTunnel login failed. To use tunnels, run:');