termbeam 1.13.6 → 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/bin/termbeam.js CHANGED
@@ -16,7 +16,8 @@ if (subcommand === 'service') {
16
16
  });
17
17
  } else if (subcommand === 'list') {
18
18
  const { list } = require('../src/cli/resume');
19
- list().catch((err) => {
19
+ const listArgs = process.argv.slice(3);
20
+ list({ json: listArgs.includes('--json') }).catch((err) => {
20
21
  console.error(err.message);
21
22
  process.exit(1);
22
23
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "termbeam",
3
- "version": "1.13.6",
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": {
package/src/cli/index.js CHANGED
@@ -11,7 +11,7 @@ termbeam — Beam your terminal to any device
11
11
  Usage:
12
12
  termbeam [options] [shell] [args...]
13
13
  termbeam resume [name] [options] Reconnect to a running session (alias: attach)
14
- termbeam list List running sessions
14
+ termbeam list [--json] List running sessions
15
15
  termbeam service <action> Manage as a background service (PM2)
16
16
 
17
17
  Actions (service):
package/src/cli/resume.js CHANGED
@@ -315,8 +315,8 @@ async function resume(args) {
315
315
  }
316
316
  }
317
317
 
318
- async function list() {
319
- log.info('Listing sessions');
318
+ async function list(options = {}) {
319
+ if (!options.json) log.info('Listing sessions');
320
320
  const saved = readConnectionConfig();
321
321
  const host = (saved && saved.host) || 'localhost';
322
322
  const port = (saved && saved.port) || 3456;
@@ -345,6 +345,10 @@ async function list() {
345
345
  process.exit(1);
346
346
  }
347
347
  } else if (err.code === 'ECONNREFUSED') {
348
+ if (options.json) {
349
+ console.log('[]');
350
+ return;
351
+ }
348
352
  console.log(dim(' No TermBeam server is running.'));
349
353
  return;
350
354
  } else {
@@ -353,6 +357,11 @@ async function list() {
353
357
  }
354
358
  }
355
359
 
360
+ if (options.json) {
361
+ console.log(JSON.stringify(sessions));
362
+ return;
363
+ }
364
+
356
365
  if (sessions.length === 0) {
357
366
  console.log(dim(` Connected to server on ${displayUrl} — no active sessions.`));
358
367
  return;
@@ -381,6 +390,8 @@ async function list() {
381
390
  );
382
391
  }
383
392
  console.log('');
393
+ console.log(dim(' Tip: use --json for machine-readable output'));
394
+ console.log('');
384
395
  }
385
396
 
386
397
  module.exports = {
@@ -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:');