@mjasano/devtunnel 1.2.0 → 1.4.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.
@@ -12,7 +12,11 @@
12
12
  "Bash(npm whoami:*)",
13
13
  "Bash(npm config set:*)",
14
14
  "Bash(npm view:*)",
15
- "Bash(npm publish:*)"
15
+ "Bash(npm publish:*)",
16
+ "Bash(git add:*)",
17
+ "Bash(git commit:*)",
18
+ "Bash(git push:*)",
19
+ "Bash(gh release create:*)"
16
20
  ]
17
21
  }
18
22
  }
@@ -0,0 +1,2 @@
1
+ node_modules
2
+ package-lock.json
package/.prettierrc ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "semi": true,
3
+ "singleQuote": true,
4
+ "tabWidth": 2,
5
+ "trailingComma": "es5",
6
+ "printWidth": 100,
7
+ "bracketSpacing": true
8
+ }
package/CHANGELOG.md CHANGED
@@ -5,6 +5,40 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.4.0] - 2026-01-02
9
+
10
+ ### Added
11
+ - Passcode authentication system
12
+ - Login page with passcode entry
13
+ - Logout button in header
14
+ - CLI `--passcode` option (auto-generate or custom)
15
+ - Auth API endpoints (`/api/auth/status`, `/api/auth/login`, `/api/auth/logout`)
16
+
17
+ ### Security
18
+ - Token-based session authentication (24h expiry)
19
+ - WebSocket connection authentication
20
+ - HTTP-only secure cookies for auth tokens
21
+
22
+ ## [1.3.0] - 2026-01-02
23
+
24
+ ### Added
25
+ - File delete and rename functionality (right-click context menu)
26
+ - File search filter in file explorer
27
+ - WebSocket security (message size limit, rate limiting)
28
+ - README.md documentation with API reference
29
+ - ESLint and Prettier configuration
30
+ - Test suite using Node.js built-in test runner
31
+
32
+ ### Changed
33
+ - Express upgraded from 4.x to 5.x
34
+ - Frontend split into separate files (styles.css, app.js)
35
+ - CLI version now reads from package.json instead of hardcoded value
36
+
37
+ ### Security
38
+ - Added 1MB max message size for WebSocket
39
+ - Added rate limiting (100 messages/second per connection)
40
+ - Added request body size limit (5MB) for JSON payloads
41
+
8
42
  ## [1.2.0] - 2025-01-02
9
43
 
10
44
  ### Added
package/README.md ADDED
@@ -0,0 +1,138 @@
1
+ # DevTunnel
2
+
3
+ Web-based terminal with integrated code editor and Cloudflare tunnel management. Access your development environment from anywhere.
4
+
5
+ ## Features
6
+
7
+ - **Web Terminal**: Full-featured terminal with xterm.js
8
+ - Multiple terminal sessions with tabs
9
+ - Session persistence (24h timeout)
10
+ - tmux session integration
11
+ - **Code Editor**: Monaco Editor (VS Code engine)
12
+ - File explorer with breadcrumb navigation
13
+ - 20+ language syntax highlighting
14
+ - Multi-file tabs with unsaved change indicators
15
+ - Keyboard shortcuts (Ctrl+S to save)
16
+ - **Tunnel Manager**: Expose local ports via Cloudflare
17
+ - Quick tunnel creation
18
+ - Real-time status updates
19
+ - Copy-to-clipboard functionality
20
+ - **System Monitor**: Real-time resource monitoring
21
+ - CPU and memory usage
22
+ - Uptime and load average
23
+
24
+ ## Installation
25
+
26
+ ```bash
27
+ npm install -g @mjasano/devtunnel
28
+ ```
29
+
30
+ ### Prerequisites
31
+
32
+ - Node.js 18+
33
+ - cloudflared (auto-installed on macOS/Linux)
34
+
35
+ ## Usage
36
+
37
+ ```bash
38
+ # Start DevTunnel with tunnel
39
+ devtunnel
40
+
41
+ # Use custom port
42
+ devtunnel --port 8080
43
+
44
+ # Enable passcode authentication (auto-generated)
45
+ devtunnel --passcode
46
+
47
+ # Enable passcode authentication (custom)
48
+ devtunnel --passcode=MYCODE
49
+
50
+ # Show help
51
+ devtunnel --help
52
+
53
+ # Show version
54
+ devtunnel --version
55
+ ```
56
+
57
+ ## Authentication
58
+
59
+ When running with `--passcode`, DevTunnel requires authentication:
60
+
61
+ - A login page is shown before accessing the terminal
62
+ - Passcode is displayed in the CLI output when auto-generated
63
+ - Sessions remain authenticated for 24 hours
64
+ - Use the Logout button in the header to end your session
65
+
66
+ ## Development
67
+
68
+ ```bash
69
+ # Clone the repository
70
+ git clone https://github.com/mjasano/web-terminal.git
71
+ cd web-terminal
72
+
73
+ # Install dependencies
74
+ npm install
75
+
76
+ # Start development server
77
+ npm start
78
+ ```
79
+
80
+ ## Docker
81
+
82
+ ```bash
83
+ # Build and run
84
+ docker-compose up -d
85
+
86
+ # Or with Docker
87
+ docker build -t devtunnel .
88
+ docker run -p 3000:3000 -v $(pwd):/workspace devtunnel
89
+ ```
90
+
91
+ ## Environment Variables
92
+
93
+ | Variable | Description | Default |
94
+ |----------|-------------|---------|
95
+ | `PORT` | Server port | 3000 |
96
+ | `WORKSPACE` | Root directory for file browser | Home directory |
97
+ | `PASSCODE` | Authentication passcode | None (no auth) |
98
+
99
+ ## API Endpoints
100
+
101
+ ### REST API
102
+
103
+ | Method | Endpoint | Description |
104
+ |--------|----------|-------------|
105
+ | GET | `/api/auth/status` | Check auth status |
106
+ | POST | `/api/auth/login` | Login with passcode |
107
+ | POST | `/api/auth/logout` | Logout |
108
+ | GET | `/api/system` | System information |
109
+ | GET | `/api/files` | File listing |
110
+ | GET | `/api/files/read` | Read file content |
111
+ | POST | `/api/files/write` | Write file content |
112
+ | POST | `/api/files/delete` | Delete file/directory |
113
+ | POST | `/api/files/rename` | Rename file/directory |
114
+ | GET | `/api/sessions` | Terminal sessions |
115
+ | DELETE | `/api/sessions/:id` | Kill session |
116
+ | GET | `/api/tunnels` | Active tunnels |
117
+ | POST | `/api/tunnels` | Create tunnel |
118
+ | DELETE | `/api/tunnels/:id` | Stop tunnel |
119
+ | GET | `/health` | Health check |
120
+
121
+ ### WebSocket Messages
122
+
123
+ | Type | Direction | Description |
124
+ |------|-----------|-------------|
125
+ | `attach` | Client | Attach to session |
126
+ | `detach` | Client | Detach from session |
127
+ | `input` | Client | Terminal input |
128
+ | `resize` | Client | Terminal resize |
129
+ | `create-tunnel` | Client | Create tunnel |
130
+ | `stop-tunnel` | Client | Stop tunnel |
131
+ | `output` | Server | Terminal output |
132
+ | `attached` | Server | Session attached |
133
+ | `sessions` | Server | Session list |
134
+ | `tunnels` | Server | Tunnel list |
135
+
136
+ ## License
137
+
138
+ MIT
package/bin/cli.js CHANGED
@@ -3,8 +3,14 @@
3
3
  const { spawn, execSync } = require('child_process');
4
4
  const path = require('path');
5
5
  const http = require('http');
6
+ const crypto = require('crypto');
6
7
 
7
- const VERSION = '1.0.0';
8
+ const { version: VERSION } = require('../package.json');
9
+
10
+ // Generate random passcode
11
+ function generatePasscode(length = 6) {
12
+ return crypto.randomBytes(length).toString('hex').slice(0, length).toUpperCase();
13
+ }
8
14
 
9
15
  // Colors
10
16
  const c = {
@@ -97,11 +103,16 @@ function waitForServer(port, timeout = 10000) {
97
103
  });
98
104
  }
99
105
 
100
- function startServer(port) {
106
+ function startServer(port, passcode = null) {
101
107
  const serverPath = path.join(__dirname, '..', 'server.js');
102
108
 
109
+ const env = { ...process.env, PORT: port };
110
+ if (passcode) {
111
+ env.PASSCODE = passcode;
112
+ }
113
+
103
114
  const server = spawn('node', [serverPath], {
104
- env: { ...process.env, PORT: port },
115
+ env,
105
116
  stdio: ['ignore', 'pipe', 'pipe'],
106
117
  detached: false
107
118
  });
@@ -151,12 +162,17 @@ function startTunnel(port) {
151
162
  });
152
163
  }
153
164
 
154
- function showSuccess(url) {
165
+ function showSuccess(url, passcode = null) {
155
166
  log('');
156
167
  log(`${c.green}${c.bold} Ready!${c.reset}`);
157
168
  log('');
158
169
  log(`${c.bold} Access from anywhere:${c.reset}`);
159
170
  log(` ${c.cyan}${c.bold}${url}${c.reset}`);
171
+ if (passcode) {
172
+ log('');
173
+ log(`${c.bold} Passcode:${c.reset}`);
174
+ log(` ${c.yellow}${c.bold}${passcode}${c.reset}`);
175
+ }
160
176
  log('');
161
177
  log(`${c.dim} Features:${c.reset}`);
162
178
  log(`${c.dim} - Web terminal with session persistence${c.reset}`);
@@ -174,11 +190,13 @@ async function main() {
174
190
  if (command === '--help' || command === '-h') {
175
191
  banner();
176
192
  log(`${c.bold}Usage:${c.reset}`);
177
- log(` devtunnel Start DevTunnel`);
178
- log(` devtunnel start Start DevTunnel`);
179
- log(` devtunnel --port 8080 Use custom port`);
180
- log(` devtunnel --help Show this help`);
181
- log(` devtunnel --version Show version`);
193
+ log(` devtunnel Start DevTunnel`);
194
+ log(` devtunnel start Start DevTunnel`);
195
+ log(` devtunnel --port 8080 Use custom port`);
196
+ log(` devtunnel --passcode Enable auth with auto-generated passcode`);
197
+ log(` devtunnel --passcode=MYCODE Enable auth with custom passcode`);
198
+ log(` devtunnel --help Show this help`);
199
+ log(` devtunnel --version Show version`);
182
200
  log('');
183
201
  return;
184
202
  }
@@ -195,6 +213,17 @@ async function main() {
195
213
  port = parseInt(args[portIndex + 1]);
196
214
  }
197
215
 
216
+ // Parse passcode
217
+ let passcode = null;
218
+ const passcodeArg = args.find(arg => arg.startsWith('--passcode'));
219
+ if (passcodeArg) {
220
+ if (passcodeArg.includes('=')) {
221
+ passcode = passcodeArg.split('=')[1];
222
+ } else {
223
+ passcode = generatePasscode();
224
+ }
225
+ }
226
+
198
227
  banner();
199
228
 
200
229
  // Check cloudflared
@@ -206,7 +235,7 @@ async function main() {
206
235
 
207
236
  // Start server
208
237
  log(`${c.dim}Starting server on port ${port}...${c.reset}`);
209
- const server = startServer(port);
238
+ const server = startServer(port, passcode);
210
239
 
211
240
  try {
212
241
  await waitForServer(port);
@@ -223,7 +252,7 @@ async function main() {
223
252
  const { tunnel, url } = await startTunnel(port);
224
253
  log(`${c.green}✓${c.reset} Tunnel created`);
225
254
 
226
- showSuccess(url);
255
+ showSuccess(url, passcode);
227
256
 
228
257
  // Handle shutdown
229
258
  const cleanup = () => {
@@ -0,0 +1,32 @@
1
+ module.exports = [
2
+ {
3
+ ignores: ['node_modules/**', 'public/app.js'],
4
+ },
5
+ {
6
+ languageOptions: {
7
+ ecmaVersion: 2022,
8
+ sourceType: 'commonjs',
9
+ globals: {
10
+ console: 'readonly',
11
+ process: 'readonly',
12
+ Buffer: 'readonly',
13
+ __dirname: 'readonly',
14
+ require: 'readonly',
15
+ module: 'readonly',
16
+ exports: 'readonly',
17
+ setInterval: 'readonly',
18
+ clearInterval: 'readonly',
19
+ setTimeout: 'readonly',
20
+ clearTimeout: 'readonly',
21
+ },
22
+ },
23
+ rules: {
24
+ 'no-unused-vars': ['error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }],
25
+ 'no-console': 'off',
26
+ 'prefer-const': 'error',
27
+ 'no-var': 'error',
28
+ eqeqeq: ['error', 'always'],
29
+ curly: ['error', 'multi-line'],
30
+ },
31
+ },
32
+ ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mjasano/devtunnel",
3
- "version": "1.2.0",
3
+ "version": "1.4.0",
4
4
  "description": "Web terminal with code editor and tunnel manager - access your dev environment from anywhere",
5
5
  "main": "server.js",
6
6
  "bin": {
@@ -8,7 +8,12 @@
8
8
  },
9
9
  "scripts": {
10
10
  "start": "node server.js",
11
- "dev": "node server.js"
11
+ "dev": "node server.js",
12
+ "lint": "eslint .",
13
+ "lint:fix": "eslint . --fix",
14
+ "format": "prettier --write .",
15
+ "format:check": "prettier --check .",
16
+ "test": "node --test"
12
17
  },
13
18
  "keywords": [
14
19
  "terminal",
@@ -21,7 +26,11 @@
21
26
  "license": "MIT",
22
27
  "dependencies": {
23
28
  "@homebridge/node-pty-prebuilt-multiarch": "^0.13.1",
24
- "express": "^4.18.2",
29
+ "express": "^5.0.1",
25
30
  "ws": "^8.14.2"
31
+ },
32
+ "devDependencies": {
33
+ "eslint": "^9.17.0",
34
+ "prettier": "^3.4.2"
26
35
  }
27
36
  }