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/README.md +56 -68
- package/bin/termbeam.js +129 -0
- package/package.json +1 -1
- package/public/terminal.html +226 -1
- package/src/cli.js +15 -0
- package/src/client.js +169 -0
- package/src/resume.js +387 -0
- package/src/routes.js +108 -2
- package/src/server.js +46 -6
- package/src/sessions.js +1 -1
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}:${
|
|
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:${
|
|
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(
|
|
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:${
|
|
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:${
|
|
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:${
|
|
266
|
+
resolve({ url: `http://localhost:${actualPort}`, defaultId });
|
|
227
267
|
});
|
|
228
268
|
});
|
|
229
269
|
}
|
package/src/sessions.js
CHANGED