termbeam 1.8.1 → 1.10.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/src/server.js CHANGED
@@ -15,6 +15,7 @@ const { setupRoutes, cleanupUploadedFiles } = require('./routes');
15
15
  const { setupWebSocket } = require('./websocket');
16
16
  const { startTunnel, cleanupTunnel, findDevtunnel } = require('./tunnel');
17
17
  const { createPreviewProxy } = require('./preview');
18
+ const { writeConnectionConfig, removeConnectionConfig } = require('./resume');
18
19
 
19
20
  // --- Helpers ---
20
21
  function getLocalIP() {
@@ -85,10 +86,30 @@ function createTermBeamServer(overrides = {}) {
85
86
  sessions.shutdown();
86
87
  cleanupUploadedFiles();
87
88
  cleanupTunnel();
89
+ removeConnectionConfig();
88
90
  server.close();
89
91
  wss.close();
90
92
  }
91
93
 
94
+ // Shutdown endpoint for --force (loopback only)
95
+ app.post('/api/shutdown', auth.middleware, (req, res) => {
96
+ const remoteAddress = req.socket && req.socket.remoteAddress;
97
+ if (
98
+ remoteAddress !== '127.0.0.1' &&
99
+ remoteAddress !== '::1' &&
100
+ remoteAddress !== '::ffff:127.0.0.1'
101
+ ) {
102
+ res.status(403).json({ error: 'Shutdown is only available from localhost' });
103
+ return;
104
+ }
105
+ res.json({ ok: true });
106
+ console.log('\n[termbeam] Shutdown requested by another instance. Goodbye!');
107
+ setTimeout(() => {
108
+ shutdown();
109
+ process.exit(0);
110
+ }, 100);
111
+ });
112
+
92
113
  async function start() {
93
114
  // If tunnel mode is on but devtunnel is missing, offer to install it
94
115
  if (config.useTunnel && !findDevtunnel()) {
@@ -134,8 +155,27 @@ function createTermBeamServer(overrides = {}) {
134
155
 
135
156
  return new Promise((resolve) => {
136
157
  server.listen(config.port, config.host, async () => {
158
+ const actualPort = server.address().port;
137
159
  const ip = getLocalIP();
138
- const localUrl = `http://${ip}:${config.port}`;
160
+ const localUrl = `http://${ip}:${actualPort}`;
161
+
162
+ // Save connection info for `termbeam resume` auto-discovery
163
+ try {
164
+ const connHost =
165
+ config.host === '0.0.0.0' ||
166
+ config.host === '127.0.0.1' ||
167
+ config.host === '::' ||
168
+ config.host === '::1'
169
+ ? 'localhost'
170
+ : config.host;
171
+ writeConnectionConfig({
172
+ port: actualPort,
173
+ host: connHost,
174
+ password: config.password || null,
175
+ });
176
+ } catch {
177
+ /* non-critical — resume will fall back to defaults */
178
+ }
139
179
 
140
180
  const defaultId = sessions.create({
141
181
  name: path.basename(config.cwd),
@@ -170,7 +210,7 @@ function createTermBeamServer(overrides = {}) {
170
210
  console.log('');
171
211
  const isLanReachable =
172
212
  config.host === '0.0.0.0' || config.host === '::' || config.host === ip;
173
- state.shareBaseUrl = isLanReachable ? localUrl : `http://localhost:${config.port}`;
213
+ state.shareBaseUrl = isLanReachable ? localUrl : `http://localhost:${actualPort}`;
174
214
  const gn = '\x1b[38;5;114m'; // green
175
215
  const _dm = '\x1b[2m'; // dim
176
216
 
@@ -178,7 +218,7 @@ function createTermBeamServer(overrides = {}) {
178
218
 
179
219
  let publicUrl = null;
180
220
  if (config.useTunnel) {
181
- const tunnel = await startTunnel(config.port, {
221
+ const tunnel = await startTunnel(actualPort, {
182
222
  persisted: config.persistedTunnel,
183
223
  anonymous: config.publicTunnel,
184
224
  });
@@ -203,12 +243,12 @@ function createTermBeamServer(overrides = {}) {
203
243
  if (publicUrl) {
204
244
  console.log(` Public: ${bl}${publicUrl}${rs}`);
205
245
  }
206
- console.log(` Local: http://localhost:${config.port}`);
246
+ console.log(` Local: http://localhost:${actualPort}`);
207
247
  if (isLanReachable) {
208
248
  console.log(` LAN: ${localUrl}`);
209
249
  }
210
250
 
211
- const qrUrl = publicUrl || (isLanReachable ? localUrl : `http://localhost:${config.port}`);
251
+ const qrUrl = publicUrl || (isLanReachable ? localUrl : `http://localhost:${actualPort}`);
212
252
  const qrDisplayUrl = qrUrl; // clean URL shown in console text
213
253
  const qrCodeUrl = config.password ? `${qrUrl}?ott=${auth.generateShareToken()}` : qrUrl;
214
254
  console.log('');
@@ -223,7 +263,7 @@ function createTermBeamServer(overrides = {}) {
223
263
  if (config.password) process.stdout.write(` Password: ${gn}${config.password}${rs}\n`);
224
264
  console.log('');
225
265
 
226
- resolve({ url: `http://localhost:${config.port}`, defaultId });
266
+ resolve({ url: `http://localhost:${actualPort}`, defaultId });
227
267
  });
228
268
  });
229
269
  }
package/src/sessions.js CHANGED
@@ -136,7 +136,7 @@ class SessionManager {
136
136
  cols,
137
137
  rows,
138
138
  cwd,
139
- env: { ...process.env, TERM: 'xterm-256color' },
139
+ env: { ...process.env, TERM: 'xterm-256color', TERMBEAM_SESSION: '1' },
140
140
  });
141
141
 
142
142
  // Send initial command once the shell is ready