cli-tunnel 1.0.0 → 1.0.2

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.
Files changed (2) hide show
  1. package/dist/index.js +74 -34
  2. package/package.json +17 -5
package/dist/index.js CHANGED
@@ -220,47 +220,87 @@ async function main() {
220
220
  console.log(` ${DIM}Port:${RESET} ${actualPort}`);
221
221
  // Tunnel
222
222
  if (hasTunnel) {
223
+ // Check if devtunnel is installed
224
+ let devtunnelInstalled = false;
223
225
  try {
224
226
  execSync('devtunnel --version', { stdio: 'pipe' });
225
- const labels = ['cli-tunnel', sanitizeLabel(sessionName || command), sanitizeLabel(repo), sanitizeLabel(branch), sanitizeLabel(machine), `port-${actualPort}`]
226
- .map(l => `--labels ${l}`).join(' ');
227
- const createOut = execSync(`devtunnel create ${labels} --expiration 1d --json`, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] });
228
- const tunnelId = JSON.parse(createOut).tunnel?.tunnelId?.split('.')[0];
229
- const cluster = JSON.parse(createOut).tunnel?.tunnelId?.split('.')[1] || 'euw';
230
- execSync(`devtunnel port create ${tunnelId} -p ${actualPort} --protocol http`, { stdio: 'pipe' });
231
- const hostProc = spawn('devtunnel', ['host', tunnelId], { stdio: 'pipe', detached: false });
232
- const url = await new Promise((resolve, reject) => {
233
- const timeout = setTimeout(() => reject(new Error('Tunnel timeout')), 15000);
234
- let out = '';
235
- hostProc.stdout?.on('data', (d) => {
236
- out += d.toString();
237
- const match = out.match(/https:\/\/[^\s]+/);
238
- if (match) {
239
- clearTimeout(timeout);
240
- resolve(match[0]);
241
- }
242
- });
243
- hostProc.on('error', (e) => { clearTimeout(timeout); reject(e); });
244
- });
245
- console.log(` ${GREEN}✓${RESET} Tunnel: ${BOLD}${url}${RESET}\n`);
246
- try {
247
- // @ts-ignore
248
- const qr = (await import('qrcode-terminal'));
249
- qr.default.generate(url, { small: true }, (code) => console.log(code));
227
+ devtunnelInstalled = true;
228
+ }
229
+ catch {
230
+ console.log(`\n ${YELLOW}⚠ devtunnel CLI not found!${RESET}\n`);
231
+ console.log(` ${BOLD}To enable remote access, install Microsoft Dev Tunnels:${RESET}\n`);
232
+ if (process.platform === 'win32') {
233
+ console.log(` ${GREEN}winget install Microsoft.devtunnel${RESET}`);
250
234
  }
251
- catch { }
252
- process.on('SIGINT', () => { hostProc.kill(); try {
253
- execSync(`devtunnel delete ${tunnelId} --force`, { stdio: 'pipe' });
235
+ else if (process.platform === 'darwin') {
236
+ console.log(` ${GREEN}brew install --cask devtunnel${RESET}`);
254
237
  }
255
- catch { } });
256
- process.on('exit', () => { hostProc.kill(); try {
257
- execSync(`devtunnel delete ${tunnelId} --force`, { stdio: 'pipe' });
238
+ else {
239
+ console.log(` ${GREEN}curl -sL https://aka.ms/DevTunnelCliInstall | bash${RESET}`);
258
240
  }
259
- catch { } });
241
+ console.log(`\n Then authenticate once:\n`);
242
+ console.log(` ${GREEN}devtunnel user login${RESET}\n`);
243
+ console.log(` ${DIM}More info: https://aka.ms/devtunnels/doc${RESET}\n`);
244
+ console.log(` ${DIM}Continuing without tunnel (local only)...${RESET}\n`);
260
245
  }
261
- catch (err) {
262
- console.log(` ${YELLOW}⚠${RESET} Tunnel failed: ${err.message}\n`);
246
+ // Check if logged in
247
+ if (devtunnelInstalled) {
248
+ try {
249
+ const userInfo = execSync('devtunnel user show', { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] });
250
+ if (userInfo.includes('not logged in') || userInfo.includes('No user')) {
251
+ throw new Error('not logged in');
252
+ }
253
+ }
254
+ catch {
255
+ console.log(`\n ${YELLOW}⚠ devtunnel not authenticated!${RESET}\n`);
256
+ console.log(` Run this once to log in:\n`);
257
+ console.log(` ${GREEN}devtunnel user login${RESET}\n`);
258
+ console.log(` ${DIM}Continuing without tunnel (local only)...${RESET}\n`);
259
+ devtunnelInstalled = false;
260
+ }
263
261
  }
262
+ if (devtunnelInstalled) {
263
+ try {
264
+ const labels = ['cli-tunnel', sanitizeLabel(sessionName || command), sanitizeLabel(repo), sanitizeLabel(branch), sanitizeLabel(machine), `port-${actualPort}`]
265
+ .map(l => `--labels ${l}`).join(' ');
266
+ const createOut = execSync(`devtunnel create ${labels} --expiration 1d --json`, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] });
267
+ const tunnelId = JSON.parse(createOut).tunnel?.tunnelId?.split('.')[0];
268
+ const cluster = JSON.parse(createOut).tunnel?.tunnelId?.split('.')[1] || 'euw';
269
+ execSync(`devtunnel port create ${tunnelId} -p ${actualPort} --protocol http`, { stdio: 'pipe' });
270
+ const hostProc = spawn('devtunnel', ['host', tunnelId], { stdio: 'pipe', detached: false });
271
+ const url = await new Promise((resolve, reject) => {
272
+ const timeout = setTimeout(() => reject(new Error('Tunnel timeout')), 15000);
273
+ let out = '';
274
+ hostProc.stdout?.on('data', (d) => {
275
+ out += d.toString();
276
+ const match = out.match(/https:\/\/[^\s]+/);
277
+ if (match) {
278
+ clearTimeout(timeout);
279
+ resolve(match[0]);
280
+ }
281
+ });
282
+ hostProc.on('error', (e) => { clearTimeout(timeout); reject(e); });
283
+ });
284
+ console.log(` ${GREEN}✓${RESET} Tunnel: ${BOLD}${url}${RESET}\n`);
285
+ try {
286
+ // @ts-ignore
287
+ const qr = (await import('qrcode-terminal'));
288
+ qr.default.generate(url, { small: true }, (code) => console.log(code));
289
+ }
290
+ catch { }
291
+ process.on('SIGINT', () => { hostProc.kill(); try {
292
+ execSync(`devtunnel delete ${tunnelId} --force`, { stdio: 'pipe' });
293
+ }
294
+ catch { } });
295
+ process.on('exit', () => { hostProc.kill(); try {
296
+ execSync(`devtunnel delete ${tunnelId} --force`, { stdio: 'pipe' });
297
+ }
298
+ catch { } });
299
+ }
300
+ catch (err) {
301
+ console.log(` ${YELLOW}⚠${RESET} Tunnel failed: ${err.message}\n`);
302
+ }
303
+ } // end if (devtunnelInstalled)
264
304
  }
265
305
  console.log(` ${DIM}Starting ${command}...${RESET}\n`);
266
306
  // Spawn PTY
package/package.json CHANGED
@@ -1,23 +1,35 @@
1
1
  {
2
2
  "name": "cli-tunnel",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Tunnel any CLI app to your phone - PTY + devtunnel + xterm.js",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "bin": {
8
- "cli-tunnel": "./dist/index.js"
8
+ "cli-tunnel": "dist/index.js"
9
9
  },
10
- "files": ["dist", "remote-ui"],
10
+ "files": [
11
+ "dist",
12
+ "remote-ui"
13
+ ],
11
14
  "scripts": {
12
15
  "build": "tsc",
13
16
  "test": "vitest run"
14
17
  },
15
- "keywords": ["cli", "tunnel", "remote", "pty", "xterm", "devtunnel", "copilot", "terminal"],
18
+ "keywords": [
19
+ "cli",
20
+ "tunnel",
21
+ "remote",
22
+ "pty",
23
+ "xterm",
24
+ "devtunnel",
25
+ "copilot",
26
+ "terminal"
27
+ ],
16
28
  "author": "Tamir Dresher",
17
29
  "license": "MIT",
18
30
  "repository": {
19
31
  "type": "git",
20
- "url": "https://github.com/tamirdresher/cli-tunnel.git"
32
+ "url": "git+https://github.com/tamirdresher/cli-tunnel.git"
21
33
  },
22
34
  "engines": {
23
35
  "node": ">=22.0.0"