termbeam 1.0.5 → 1.0.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.
- package/package.json +1 -1
- package/src/routes.js +12 -11
- package/src/tunnel.js +50 -11
package/package.json
CHANGED
package/src/routes.js
CHANGED
|
@@ -45,9 +45,9 @@ function setupRoutes(app, { auth, sessions, config }) {
|
|
|
45
45
|
});
|
|
46
46
|
|
|
47
47
|
// Pages
|
|
48
|
-
app.get('/', auth.middleware, (_req, res) => res.sendFile(
|
|
48
|
+
app.get('/', auth.middleware, (_req, res) => res.sendFile('index.html', { root: PUBLIC_DIR }));
|
|
49
49
|
app.get('/terminal', auth.middleware, (_req, res) =>
|
|
50
|
-
res.sendFile(
|
|
50
|
+
res.sendFile('terminal.html', { root: PUBLIC_DIR }),
|
|
51
51
|
);
|
|
52
52
|
|
|
53
53
|
// Session API
|
|
@@ -61,7 +61,7 @@ function setupRoutes(app, { auth, sessions, config }) {
|
|
|
61
61
|
// Validate shell field
|
|
62
62
|
if (shell) {
|
|
63
63
|
const availableShells = detectShells();
|
|
64
|
-
const isValid = availableShells.some(s => s.path === shell || s.cmd === shell);
|
|
64
|
+
const isValid = availableShells.some((s) => s.path === shell || s.cmd === shell);
|
|
65
65
|
if (!isValid) {
|
|
66
66
|
return res.status(400).json({ error: 'Invalid shell' });
|
|
67
67
|
}
|
|
@@ -150,13 +150,14 @@ function setupRoutes(app, { auth, sessions, config }) {
|
|
|
150
150
|
if (!buffer.length) {
|
|
151
151
|
return res.status(400).json({ error: 'No image data' });
|
|
152
152
|
}
|
|
153
|
-
const ext =
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
153
|
+
const ext =
|
|
154
|
+
{
|
|
155
|
+
'image/png': '.png',
|
|
156
|
+
'image/jpeg': '.jpg',
|
|
157
|
+
'image/gif': '.gif',
|
|
158
|
+
'image/webp': '.webp',
|
|
159
|
+
'image/bmp': '.bmp',
|
|
160
|
+
}[contentType] || '.png';
|
|
160
161
|
const filename = `termbeam-${crypto.randomUUID()}${ext}`;
|
|
161
162
|
const filepath = path.join(os.tmpdir(), filename);
|
|
162
163
|
fs.writeFileSync(filepath, buffer);
|
|
@@ -173,7 +174,7 @@ function setupRoutes(app, { auth, sessions, config }) {
|
|
|
173
174
|
|
|
174
175
|
// Directory listing for folder browser
|
|
175
176
|
app.get('/api/dirs', auth.middleware, (req, res) => {
|
|
176
|
-
const query = req.query.q ||
|
|
177
|
+
const query = req.query.q || config.cwd + path.sep;
|
|
177
178
|
const endsWithSep = query.endsWith('/') || query.endsWith('\\');
|
|
178
179
|
const dir = endsWithSep ? query : path.dirname(query);
|
|
179
180
|
const prefix = endsWithSep ? '' : path.basename(query);
|
package/src/tunnel.js
CHANGED
|
@@ -47,7 +47,10 @@ function loadPersistedTunnel() {
|
|
|
47
47
|
|
|
48
48
|
function savePersistedTunnel(id) {
|
|
49
49
|
fs.mkdirSync(TUNNEL_CONFIG_DIR, { recursive: true });
|
|
50
|
-
fs.writeFileSync(
|
|
50
|
+
fs.writeFileSync(
|
|
51
|
+
TUNNEL_CONFIG_PATH,
|
|
52
|
+
JSON.stringify({ tunnelId: id, createdAt: new Date().toISOString() }, null, 2),
|
|
53
|
+
);
|
|
51
54
|
}
|
|
52
55
|
|
|
53
56
|
function deletePersisted() {
|
|
@@ -68,7 +71,10 @@ function deletePersisted() {
|
|
|
68
71
|
function isTunnelValid(id) {
|
|
69
72
|
try {
|
|
70
73
|
if (!SAFE_ID_RE.test(id)) return false;
|
|
71
|
-
execFileSync(devtunnelCmd, ['show', id, '--json'], {
|
|
74
|
+
execFileSync(devtunnelCmd, ['show', id, '--json'], {
|
|
75
|
+
encoding: 'utf-8',
|
|
76
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
77
|
+
});
|
|
72
78
|
return true;
|
|
73
79
|
} catch {
|
|
74
80
|
return false;
|
|
@@ -87,7 +93,9 @@ async function startTunnel(port, options = {}) {
|
|
|
87
93
|
log.error('');
|
|
88
94
|
log.error(' Install it:');
|
|
89
95
|
log.error(' Windows: winget install Microsoft.devtunnel');
|
|
90
|
-
log.error(
|
|
96
|
+
log.error(
|
|
97
|
+
' or: Invoke-WebRequest -Uri https://aka.ms/TunnelsCliDownload/win-x64 -OutFile devtunnel.exe',
|
|
98
|
+
);
|
|
91
99
|
log.error(' macOS: brew install --cask devtunnel');
|
|
92
100
|
log.error(' Linux: curl -sL https://aka.ms/DevTunnelCliInstall | bash');
|
|
93
101
|
log.error('');
|
|
@@ -103,7 +111,10 @@ async function startTunnel(port, options = {}) {
|
|
|
103
111
|
// Ensure user is logged in
|
|
104
112
|
let loggedIn = false;
|
|
105
113
|
try {
|
|
106
|
-
const userOut = execFileSync(devtunnelCmd, ['user', 'show'], {
|
|
114
|
+
const userOut = execFileSync(devtunnelCmd, ['user', 'show'], {
|
|
115
|
+
encoding: 'utf-8',
|
|
116
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
117
|
+
});
|
|
107
118
|
// user show can succeed but show "not logged in" status
|
|
108
119
|
loggedIn = userOut && !userOut.toLowerCase().includes('not logged in');
|
|
109
120
|
} catch {}
|
|
@@ -111,7 +122,18 @@ async function startTunnel(port, options = {}) {
|
|
|
111
122
|
if (!loggedIn) {
|
|
112
123
|
log.info('devtunnel not logged in, launching login...');
|
|
113
124
|
log.info('A browser window will open for authentication.');
|
|
114
|
-
|
|
125
|
+
try {
|
|
126
|
+
execFileSync(devtunnelCmd, ['user', 'login'], { stdio: 'inherit' });
|
|
127
|
+
} catch (loginErr) {
|
|
128
|
+
log.error('');
|
|
129
|
+
log.error(' DevTunnel login failed. To use tunnels, run:');
|
|
130
|
+
log.error(' devtunnel user login');
|
|
131
|
+
log.error('');
|
|
132
|
+
log.error(' Or start without a tunnel:');
|
|
133
|
+
log.error(' termbeam --no-tunnel');
|
|
134
|
+
log.error('');
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
115
137
|
}
|
|
116
138
|
|
|
117
139
|
const persisted = options.persisted;
|
|
@@ -130,7 +152,9 @@ async function startTunnel(port, options = {}) {
|
|
|
130
152
|
if (saved) {
|
|
131
153
|
log.info('Persisted tunnel expired, creating new one');
|
|
132
154
|
}
|
|
133
|
-
const createOut = execFileSync(devtunnelCmd, ['create', '--expiration', '30d', '--json'], {
|
|
155
|
+
const createOut = execFileSync(devtunnelCmd, ['create', '--expiration', '30d', '--json'], {
|
|
156
|
+
encoding: 'utf-8',
|
|
157
|
+
});
|
|
134
158
|
const tunnelData = JSON.parse(createOut);
|
|
135
159
|
tunnelId = tunnelData.tunnel.tunnelId;
|
|
136
160
|
savePersistedTunnel(tunnelId);
|
|
@@ -140,7 +164,9 @@ async function startTunnel(port, options = {}) {
|
|
|
140
164
|
tunnelMode = 'ephemeral';
|
|
141
165
|
tunnelExpiry = '1 day';
|
|
142
166
|
// Ephemeral tunnel — create fresh, will be deleted on shutdown
|
|
143
|
-
const createOut = execFileSync(devtunnelCmd, ['create', '--expiration', '1d', '--json'], {
|
|
167
|
+
const createOut = execFileSync(devtunnelCmd, ['create', '--expiration', '1d', '--json'], {
|
|
168
|
+
encoding: 'utf-8',
|
|
169
|
+
});
|
|
144
170
|
const tunnelData = JSON.parse(createOut);
|
|
145
171
|
tunnelId = tunnelData.tunnel.tunnelId;
|
|
146
172
|
log.info(`Created ephemeral tunnel ${tunnelId}`);
|
|
@@ -148,10 +174,18 @@ async function startTunnel(port, options = {}) {
|
|
|
148
174
|
|
|
149
175
|
// Idempotent port and access setup
|
|
150
176
|
try {
|
|
151
|
-
execFileSync(
|
|
177
|
+
execFileSync(
|
|
178
|
+
devtunnelCmd,
|
|
179
|
+
['port', 'create', tunnelId, '-p', String(port), '--protocol', 'http'],
|
|
180
|
+
{ stdio: 'pipe' },
|
|
181
|
+
);
|
|
152
182
|
} catch {}
|
|
153
183
|
try {
|
|
154
|
-
execFileSync(
|
|
184
|
+
execFileSync(
|
|
185
|
+
devtunnelCmd,
|
|
186
|
+
['access', 'create', tunnelId, '-p', String(port), '--anonymous'],
|
|
187
|
+
{ stdio: 'pipe' },
|
|
188
|
+
);
|
|
155
189
|
} catch {}
|
|
156
190
|
|
|
157
191
|
const hostProc = spawn(devtunnelCmd, ['host', tunnelId], {
|
|
@@ -193,8 +227,13 @@ function cleanupTunnel() {
|
|
|
193
227
|
// On Windows, kill the process tree to ensure all children die
|
|
194
228
|
if (process.platform === 'win32' && tunnelProc.pid) {
|
|
195
229
|
try {
|
|
196
|
-
execFileSync('taskkill', ['/pid', String(tunnelProc.pid), '/T', '/F'], {
|
|
197
|
-
|
|
230
|
+
execFileSync('taskkill', ['/pid', String(tunnelProc.pid), '/T', '/F'], {
|
|
231
|
+
stdio: 'pipe',
|
|
232
|
+
timeout: 5000,
|
|
233
|
+
});
|
|
234
|
+
} catch {
|
|
235
|
+
/* best effort */
|
|
236
|
+
}
|
|
198
237
|
} else {
|
|
199
238
|
tunnelProc.kill('SIGKILL');
|
|
200
239
|
}
|