instatunnel 1.0.1
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 +69 -0
- package/install.js +271 -0
- package/package.json +79 -0
package/README.md
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# InstaTunnel CLI
|
|
2
|
+
|
|
3
|
+
> Expose your localhost to the internet instantly - the ngrok alternative that's 40% cheaper
|
|
4
|
+
|
|
5
|
+
## 🚀 Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Install globally
|
|
9
|
+
npm install -g instatunnel
|
|
10
|
+
|
|
11
|
+
# Share your app instantly
|
|
12
|
+
instatunnel 3000
|
|
13
|
+
# or use the short alias
|
|
14
|
+
it 3000
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## ✨ Features
|
|
18
|
+
|
|
19
|
+
- **🆓 Anonymous usage** - No signup required
|
|
20
|
+
- **⚡ Instant setup** - Works in under 30 seconds
|
|
21
|
+
- **🕐 24-hour sessions** - vs ngrok's 2-hour limit
|
|
22
|
+
- **🎨 Custom subdomains** - Free vs ngrok's paid feature
|
|
23
|
+
- **📱 QR codes** - Perfect for mobile testing
|
|
24
|
+
- **🔒 Password protection** - Secure your tunnels
|
|
25
|
+
- **🎯 Framework detection** - Auto-detect React, Next.js, Laravel
|
|
26
|
+
|
|
27
|
+
## 📖 Usage Examples
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# Basic tunneling
|
|
31
|
+
instatunnel 3000 # Share localhost:3000
|
|
32
|
+
it 8080 # Short alias for port 8080
|
|
33
|
+
|
|
34
|
+
# Framework shortcuts
|
|
35
|
+
instatunnel --react # Auto-detect React (port 3000)
|
|
36
|
+
instatunnel --next # Auto-detect Next.js (port 3000)
|
|
37
|
+
instatunnel --laravel # Auto-detect Laravel (port 8000)
|
|
38
|
+
|
|
39
|
+
# Security features
|
|
40
|
+
instatunnel 3000 --password secret # Password protect tunnel
|
|
41
|
+
instatunnel 3000 --auth user:pass # Basic authentication
|
|
42
|
+
|
|
43
|
+
# Mobile testing
|
|
44
|
+
instatunnel 3000 --qr # Show QR code for mobile
|
|
45
|
+
|
|
46
|
+
# Management
|
|
47
|
+
instatunnel --list # List active tunnels
|
|
48
|
+
instatunnel --kill myapp # Stop specific tunnel
|
|
49
|
+
instatunnel --share # Generate sharing templates
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## 🆚 vs ngrok
|
|
53
|
+
|
|
54
|
+
| Feature | InstaTunnel | ngrok |
|
|
55
|
+
|---------|-------------|-------|
|
|
56
|
+
| **Anonymous usage** | ✅ No signup | ❌ Account required |
|
|
57
|
+
| **Session duration** | ✅ 24 hours | ❌ 2 hours |
|
|
58
|
+
| **Custom subdomains** | ✅ Free | ❌ Paid only |
|
|
59
|
+
| **Multiple tunnels** | ✅ 3 free | ❌ 1 free |
|
|
60
|
+
| **Setup time** | ✅ < 30 seconds | ❌ 2-3 minutes |
|
|
61
|
+
| **Pricing** | ✅ 40% cheaper | ❌ Expensive |
|
|
62
|
+
|
|
63
|
+
## 🌐 Website
|
|
64
|
+
|
|
65
|
+
Visit [instatunnel.my](https://instatunnel.my) for more information.
|
|
66
|
+
|
|
67
|
+
## 📝 License
|
|
68
|
+
|
|
69
|
+
MIT
|
package/install.js
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const https = require('https');
|
|
6
|
+
const os = require('os');
|
|
7
|
+
const { execSync } = require('child_process');
|
|
8
|
+
|
|
9
|
+
const BINARY_NAME = 'instatunnel';
|
|
10
|
+
const PRIMARY_URL = 'https://instatunnel.my/releases';
|
|
11
|
+
const FALLBACK_URL = 'https://github.com/instatunnel/cli/releases/latest/download';
|
|
12
|
+
|
|
13
|
+
// Colors for console output
|
|
14
|
+
const colors = {
|
|
15
|
+
reset: '\x1b[0m',
|
|
16
|
+
red: '\x1b[31m',
|
|
17
|
+
green: '\x1b[32m',
|
|
18
|
+
yellow: '\x1b[33m',
|
|
19
|
+
blue: '\x1b[34m',
|
|
20
|
+
cyan: '\x1b[36m'
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
function log(message, color = 'reset') {
|
|
24
|
+
console.log(`${colors[color]}${message}${colors.reset}`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function detectPlatform() {
|
|
28
|
+
const platform = os.platform();
|
|
29
|
+
const arch = os.arch();
|
|
30
|
+
|
|
31
|
+
let platformSuffix;
|
|
32
|
+
switch (platform) {
|
|
33
|
+
case 'linux':
|
|
34
|
+
platformSuffix = 'linux';
|
|
35
|
+
break;
|
|
36
|
+
case 'darwin':
|
|
37
|
+
platformSuffix = 'darwin';
|
|
38
|
+
break;
|
|
39
|
+
case 'win32':
|
|
40
|
+
platformSuffix = 'windows';
|
|
41
|
+
break;
|
|
42
|
+
case 'freebsd':
|
|
43
|
+
platformSuffix = 'freebsd';
|
|
44
|
+
break;
|
|
45
|
+
case 'openbsd':
|
|
46
|
+
platformSuffix = 'openbsd';
|
|
47
|
+
break;
|
|
48
|
+
default:
|
|
49
|
+
throw new Error(`Unsupported platform: ${platform}`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
let archSuffix;
|
|
53
|
+
switch (arch) {
|
|
54
|
+
case 'x64':
|
|
55
|
+
archSuffix = 'amd64';
|
|
56
|
+
break;
|
|
57
|
+
case 'arm64':
|
|
58
|
+
archSuffix = 'arm64';
|
|
59
|
+
break;
|
|
60
|
+
case 'ia32':
|
|
61
|
+
archSuffix = '386';
|
|
62
|
+
break;
|
|
63
|
+
default:
|
|
64
|
+
throw new Error(`Unsupported architecture: ${arch}`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const extension = platform === 'win32' ? '.exe' : '';
|
|
68
|
+
return `${BINARY_NAME}-${platformSuffix}-${archSuffix}${extension}`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function downloadFile(url, outputPath) {
|
|
72
|
+
return new Promise((resolve, reject) => {
|
|
73
|
+
const file = fs.createWriteStream(outputPath);
|
|
74
|
+
|
|
75
|
+
https.get(url, (response) => {
|
|
76
|
+
// Handle redirects
|
|
77
|
+
if (response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) {
|
|
78
|
+
file.close();
|
|
79
|
+
fs.unlinkSync(outputPath);
|
|
80
|
+
return downloadFile(response.headers.location, outputPath).then(resolve).catch(reject);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (response.statusCode !== 200) {
|
|
84
|
+
file.close();
|
|
85
|
+
fs.unlinkSync(outputPath);
|
|
86
|
+
reject(new Error(`HTTP ${response.statusCode}: ${response.statusMessage}`));
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
response.pipe(file);
|
|
91
|
+
|
|
92
|
+
file.on('finish', () => {
|
|
93
|
+
file.close(resolve);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
file.on('error', (err) => {
|
|
97
|
+
file.close();
|
|
98
|
+
fs.unlinkSync(outputPath);
|
|
99
|
+
reject(err);
|
|
100
|
+
});
|
|
101
|
+
}).on('error', (err) => {
|
|
102
|
+
reject(err);
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async function downloadBinary() {
|
|
108
|
+
const binaryName = detectPlatform();
|
|
109
|
+
const binDir = path.join(__dirname, 'bin');
|
|
110
|
+
const isWindows = os.platform() === 'win32';
|
|
111
|
+
const outputPath = path.join(binDir, isWindows ? 'instatunnel.exe' : 'instatunnel');
|
|
112
|
+
|
|
113
|
+
// Create bin directory
|
|
114
|
+
if (!fs.existsSync(binDir)) {
|
|
115
|
+
fs.mkdirSync(binDir, { recursive: true });
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
log('🚀 Installing InstaTunnel CLI...', 'blue');
|
|
119
|
+
log(`📋 Platform: ${os.platform()}-${os.arch()}`, 'cyan');
|
|
120
|
+
|
|
121
|
+
// Try primary URL first
|
|
122
|
+
const primaryUrl = `${PRIMARY_URL}/${binaryName}`;
|
|
123
|
+
const fallbackUrl = `${FALLBACK_URL}/${binaryName}`;
|
|
124
|
+
|
|
125
|
+
try {
|
|
126
|
+
log('📥 Downloading from primary source...', 'blue');
|
|
127
|
+
await downloadFile(primaryUrl, outputPath);
|
|
128
|
+
} catch (primaryError) {
|
|
129
|
+
log(`⚠️ Primary download failed: ${primaryError.message}`, 'yellow');
|
|
130
|
+
log('📥 Trying fallback source (GitHub)...', 'blue');
|
|
131
|
+
|
|
132
|
+
try {
|
|
133
|
+
await downloadFile(fallbackUrl, outputPath);
|
|
134
|
+
} catch (fallbackError) {
|
|
135
|
+
throw new Error(`Both downloads failed:\n Primary: ${primaryError.message}\n Fallback: ${fallbackError.message}`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Verify binary was downloaded and is valid
|
|
140
|
+
if (!fs.existsSync(outputPath) || fs.statSync(outputPath).size === 0) {
|
|
141
|
+
throw new Error('Downloaded binary is invalid or empty');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Make executable on Unix systems
|
|
145
|
+
if (!isWindows) {
|
|
146
|
+
fs.chmodSync(outputPath, 0o755);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Create 'it' alias
|
|
150
|
+
const itPath = path.join(binDir, isWindows ? 'it.exe' : 'it');
|
|
151
|
+
fs.copyFileSync(outputPath, itPath);
|
|
152
|
+
|
|
153
|
+
log('✅ InstaTunnel CLI installed successfully!', 'green');
|
|
154
|
+
log('', 'reset');
|
|
155
|
+
log('🚀 Quick Start:', 'cyan');
|
|
156
|
+
log(' instatunnel 3000 # Expose port 3000 instantly (no setup needed!)', 'reset');
|
|
157
|
+
log(' it 3000 # Short alias for the same command', 'reset');
|
|
158
|
+
log('', 'reset');
|
|
159
|
+
log('💡 More examples:', 'cyan');
|
|
160
|
+
log(' instatunnel # Auto-detect common ports (3000, 8000, 8080)', 'reset');
|
|
161
|
+
log(' instatunnel 8080 --name myapp', 'reset');
|
|
162
|
+
log(' instatunnel --help # See all options', 'reset');
|
|
163
|
+
log('', 'reset');
|
|
164
|
+
log('🌟 Key Benefits:', 'yellow');
|
|
165
|
+
log(' ✓ No signup required - works instantly', 'green');
|
|
166
|
+
log(' ✓ 24+ hour sessions (vs ngrok\'s 2 hours)', 'green');
|
|
167
|
+
log(' ✓ Custom subdomains included free', 'green');
|
|
168
|
+
log(' ✓ 40% cheaper than ngrok', 'green');
|
|
169
|
+
log('', 'reset');
|
|
170
|
+
log('📚 Documentation: https://docs.instatunnel.my', 'yellow');
|
|
171
|
+
|
|
172
|
+
// Test installation
|
|
173
|
+
try {
|
|
174
|
+
const testCommand = isWindows ? `"${outputPath}" --version` : `${outputPath} --version`;
|
|
175
|
+
const version = execSync(testCommand, { encoding: 'utf8', timeout: 5000 }).trim();
|
|
176
|
+
log(`🔧 Installation verified! Version: ${version}`, 'green');
|
|
177
|
+
} catch (testError) {
|
|
178
|
+
log('ℹ️ Installation complete - restart terminal if command not found', 'yellow');
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Create uninstall script
|
|
183
|
+
function createUninstallScript() {
|
|
184
|
+
const uninstallPath = path.join(__dirname, 'uninstall.js');
|
|
185
|
+
const uninstallScript = `#!/usr/bin/env node
|
|
186
|
+
|
|
187
|
+
const fs = require('fs');
|
|
188
|
+
const path = require('path');
|
|
189
|
+
|
|
190
|
+
function cleanup() {
|
|
191
|
+
const binDir = path.join(__dirname, 'bin');
|
|
192
|
+
|
|
193
|
+
try {
|
|
194
|
+
if (fs.existsSync(binDir)) {
|
|
195
|
+
fs.rmSync(binDir, { recursive: true, force: true });
|
|
196
|
+
}
|
|
197
|
+
console.log('✅ InstaTunnel CLI uninstalled successfully!');
|
|
198
|
+
} catch (error) {
|
|
199
|
+
console.warn('⚠️ Some cleanup may be incomplete:', error.message);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (require.main === module) {
|
|
204
|
+
cleanup();
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
module.exports = { cleanup };
|
|
208
|
+
`;
|
|
209
|
+
|
|
210
|
+
fs.writeFileSync(uninstallPath, uninstallScript);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Create test script
|
|
214
|
+
function createTestScript() {
|
|
215
|
+
const testPath = path.join(__dirname, 'test.js');
|
|
216
|
+
const testScript = `#!/usr/bin/env node
|
|
217
|
+
|
|
218
|
+
const { execSync } = require('child_process');
|
|
219
|
+
const path = require('path');
|
|
220
|
+
const os = require('os');
|
|
221
|
+
|
|
222
|
+
function test() {
|
|
223
|
+
const binDir = path.join(__dirname, 'bin');
|
|
224
|
+
const isWindows = os.platform() === 'win32';
|
|
225
|
+
const instatunnelPath = path.join(binDir, isWindows ? 'instatunnel.exe' : 'instatunnel');
|
|
226
|
+
const itPath = path.join(binDir, isWindows ? 'it.exe' : 'it');
|
|
227
|
+
|
|
228
|
+
try {
|
|
229
|
+
// Test main command
|
|
230
|
+
const helpOutput = execSync(\`"\${instatunnelPath}" --help\`, { encoding: 'utf8', timeout: 5000 });
|
|
231
|
+
console.log('✅ instatunnel command works');
|
|
232
|
+
|
|
233
|
+
// Test alias
|
|
234
|
+
const itHelpOutput = execSync(\`"\${itPath}" --help\`, { encoding: 'utf8', timeout: 5000 });
|
|
235
|
+
console.log('✅ it alias works');
|
|
236
|
+
|
|
237
|
+
console.log('✅ All tests passed!');
|
|
238
|
+
return true;
|
|
239
|
+
} catch (error) {
|
|
240
|
+
console.error('❌ Test failed:', error.message);
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (require.main === module) {
|
|
246
|
+
const success = test();
|
|
247
|
+
process.exit(success ? 0 : 1);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
module.exports = { test };
|
|
251
|
+
`;
|
|
252
|
+
|
|
253
|
+
fs.writeFileSync(testPath, testScript);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (require.main === module) {
|
|
257
|
+
downloadBinary()
|
|
258
|
+
.then(() => {
|
|
259
|
+
createUninstallScript();
|
|
260
|
+
createTestScript();
|
|
261
|
+
})
|
|
262
|
+
.catch((err) => {
|
|
263
|
+
log(`❌ Installation failed: ${err.message}`, 'red');
|
|
264
|
+
log('', 'reset');
|
|
265
|
+
log('🔧 Troubleshooting:', 'yellow');
|
|
266
|
+
log(' • Check your internet connection', 'reset');
|
|
267
|
+
log(' • Try running: npm install -g instatunnel --verbose', 'reset');
|
|
268
|
+
log(' • Report issues: https://github.com/instatunnel/cli/issues', 'reset');
|
|
269
|
+
process.exit(1);
|
|
270
|
+
});
|
|
271
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "instatunnel",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Expose your localhost to the internet instantly - the ngrok alternative that's 40% cheaper with superior UX",
|
|
5
|
+
"main": "install.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"instatunnel": "./bin/instatunnel",
|
|
8
|
+
"it": "./bin/it"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"postinstall": "node install.js",
|
|
12
|
+
"test": "node test.js",
|
|
13
|
+
"preuninstall": "node uninstall.js"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"ngrok",
|
|
17
|
+
"tunnel",
|
|
18
|
+
"localhost",
|
|
19
|
+
"development",
|
|
20
|
+
"webhook",
|
|
21
|
+
"cli",
|
|
22
|
+
"instant",
|
|
23
|
+
"port-forwarding",
|
|
24
|
+
"demo",
|
|
25
|
+
"testing",
|
|
26
|
+
"react",
|
|
27
|
+
"nextjs",
|
|
28
|
+
"laravel",
|
|
29
|
+
"api",
|
|
30
|
+
"free",
|
|
31
|
+
"anonymous",
|
|
32
|
+
"zero-config"
|
|
33
|
+
],
|
|
34
|
+
"author": {
|
|
35
|
+
"name": "InstaTunnel Team",
|
|
36
|
+
"email": "hello@instatunnel.my",
|
|
37
|
+
"url": "https://instatunnel.my"
|
|
38
|
+
},
|
|
39
|
+
"license": "MIT",
|
|
40
|
+
"homepage": "https://instatunnel.my",
|
|
41
|
+
"repository": {
|
|
42
|
+
"type": "git",
|
|
43
|
+
"url": "git+https://github.com/instatunnel/cli.git"
|
|
44
|
+
},
|
|
45
|
+
"bugs": {
|
|
46
|
+
"url": "https://github.com/instatunnel/cli/issues"
|
|
47
|
+
},
|
|
48
|
+
"funding": {
|
|
49
|
+
"type": "opencollective",
|
|
50
|
+
"url": "https://opencollective.com/instatunnel"
|
|
51
|
+
},
|
|
52
|
+
"engines": {
|
|
53
|
+
"node": ">=14.0.0",
|
|
54
|
+
"npm": ">=6.0.0"
|
|
55
|
+
},
|
|
56
|
+
"os": [
|
|
57
|
+
"linux",
|
|
58
|
+
"darwin",
|
|
59
|
+
"win32",
|
|
60
|
+
"freebsd",
|
|
61
|
+
"openbsd"
|
|
62
|
+
],
|
|
63
|
+
"cpu": [
|
|
64
|
+
"x64",
|
|
65
|
+
"arm64",
|
|
66
|
+
"ia32"
|
|
67
|
+
],
|
|
68
|
+
"preferGlobal": true,
|
|
69
|
+
"files": [
|
|
70
|
+
"install.js",
|
|
71
|
+
"uninstall.js",
|
|
72
|
+
"test.js",
|
|
73
|
+
"bin/",
|
|
74
|
+
"README.md"
|
|
75
|
+
],
|
|
76
|
+
"directories": {
|
|
77
|
+
"bin": "./bin"
|
|
78
|
+
}
|
|
79
|
+
}
|