xpose-cli 0.1.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/LICENSE +21 -0
- package/README.md +112 -0
- package/bin/cli.js +66 -0
- package/bin/install.js +79 -0
- package/package.json +32 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 mt-tunnel-cli Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Cloudflare Tunnel CLI (xpose)
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+
|
|
6
|
+
A lightning-fast, zero-config, terminal UI (TUI) wrapper for Cloudflare Tunnels (`cloudflared`). Written entirely in Rust 🦀, distributed via NPM.
|
|
7
|
+
|
|
8
|
+
`xpose` takes the pain out of exposing your local development servers to the internet. It automatically handles binary downloads, tunnel management, and provides a beautiful, hacker-style real-time dashboard.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 🚀 Features
|
|
13
|
+
|
|
14
|
+
- **Zero-Config**: Just type `xpose <port>` and you're online. No accounts or complex setups needed.
|
|
15
|
+
- **Vivid Terminal UI**: Real-time ASCII sparkline charts (`[ ▃▅▇█▆▄ ]`), traffic monitoring (Rx/Tx), and ping latency.
|
|
16
|
+
- **Developer Convenience**:
|
|
17
|
+
- Generates a **QR Code** directly in your terminal for instant mobile testing.
|
|
18
|
+
- **Auto-copies** the public URL to your clipboard.
|
|
19
|
+
- Automatically scans common ports (3000, 8000, 8080) if no port is provided.
|
|
20
|
+
- **Smart Cloudflared Management**:
|
|
21
|
+
- Downloads the correct statically linked binary for your OS and Architecture (Windows, macOS Intel/ARM, Linux).
|
|
22
|
+
- Background "Tunnel Pooling" avoids Cloudflare limits and ensures instant connections.
|
|
23
|
+
- **Hardened Security**:
|
|
24
|
+
- Built-in IP Rate Limiting.
|
|
25
|
+
- Automatic Phishing prevention (blocks sensitive subdomains like `login`, `bank`).
|
|
26
|
+
- Restricts exposure to safe development ports only.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 📦 Installation
|
|
31
|
+
|
|
32
|
+
Since `xpose` is distributed as an NPM package, installation is as simple as:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npm install -g xpose-cli
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
*(Note: The NPM wrapper automatically fetches the blazing-fast Rust binary optimized for your system).*
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## 💻 Usage
|
|
43
|
+
|
|
44
|
+
Expose a local port to the internet instantly:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# Expose port 3000 (TCP)
|
|
48
|
+
xpose 3000
|
|
49
|
+
|
|
50
|
+
# Expose port 8080 via UDP
|
|
51
|
+
xpose 8080 --udp
|
|
52
|
+
|
|
53
|
+
# Auto-detect (scans 3000, 8000, 8080 or reads MT_TUNNEL_PORT from .env)
|
|
54
|
+
xpose
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## 🏗️ Architecture
|
|
60
|
+
|
|
61
|
+
The project consists of two core components, both written in Rust:
|
|
62
|
+
|
|
63
|
+
### 1. The CLI Client (`packages/cli`)
|
|
64
|
+
A Rust application utilizing `tokio` for async operations, `reqwest` for API communication, and `crossterm`/`indicatif` for the vivid ASCII user interface. It acts as an intelligent wrapper around the official `cloudflared` binary.
|
|
65
|
+
|
|
66
|
+
### 2. The Key Server (`packages/key-server`)
|
|
67
|
+
A Cloudflare Worker written in Rust (`workers-rs`) using D1 (SQLite) for state management.
|
|
68
|
+
- Maintains a pool of ready-to-use Cloudflare Tunnels (Quick Tunnel instances).
|
|
69
|
+
- Manages sub-domain leasing to connected clients.
|
|
70
|
+
- Enforces security rules (IP Rate Limiting, Keyword Filtering, Port Restrictions).
|
|
71
|
+
- Automatic garbage collection of dead tunnels via Cron triggers.
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## 🛠️ Development Setup
|
|
76
|
+
|
|
77
|
+
If you wish to build the project from source:
|
|
78
|
+
|
|
79
|
+
### Prerequisites
|
|
80
|
+
- Docker and Docker Compose installed on your host machine.
|
|
81
|
+
|
|
82
|
+
### Docker Development Environment (Required)
|
|
83
|
+
For a consistent environment across all platforms, **all development, building, and testing must be done via Docker**. Do NOT run `cargo` or `npm` directly on your host machine.
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
# 1. Start the development container in the background
|
|
87
|
+
docker-compose up -d
|
|
88
|
+
|
|
89
|
+
# 2. Enter the container shell
|
|
90
|
+
docker-compose exec dev bash
|
|
91
|
+
|
|
92
|
+
# 3. Inside the container, you can run cargo/npm normally:
|
|
93
|
+
cd packages/cli
|
|
94
|
+
cargo build --release # Build the CLI
|
|
95
|
+
cargo test # Run tests
|
|
96
|
+
|
|
97
|
+
cd /workspace/packages/key-server
|
|
98
|
+
npm install # Install dependencies
|
|
99
|
+
wrangler d1 migrations apply DB --local # Run database migrations
|
|
100
|
+
wrangler dev # Start the Key Server locally
|
|
101
|
+
|
|
102
|
+
# If you need to build the standalone Linux binary:
|
|
103
|
+
cargo build --release --target x86_64-unknown-linux-musl
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## 📝 License
|
|
109
|
+
|
|
110
|
+
This project is licensed under the MIT License.
|
|
111
|
+
|
|
112
|
+
By using this tool, you also agree to the Cloudflare Terms of Service as `xpose` acts as a wrapper for `cloudflared` (Apache-2.0 License). The CLI will automatically fetch and store the required Cloudflare license upon first run.
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { spawn } = require('child_process');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const os = require('os');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Tunnel CLI NPM Wrapper
|
|
10
|
+
* This script identifies the appropriate Rust binary for the current system
|
|
11
|
+
* and executes it with passed arguments.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
function getBinaryPath() {
|
|
15
|
+
const platform = os.platform();
|
|
16
|
+
const arch = os.arch();
|
|
17
|
+
|
|
18
|
+
// In a real production scenario, these would be downloaded on install
|
|
19
|
+
// For this environment, we check local build path or a pre-defined location.
|
|
20
|
+
|
|
21
|
+
let binName = platform === 'win32' ? 'xpose.exe' : 'xpose';
|
|
22
|
+
|
|
23
|
+
// Check locally built binary first (for development/demo)
|
|
24
|
+
const localBuild = path.join(__dirname, '..', 'target', 'release', binName);
|
|
25
|
+
if (fs.existsSync(localBuild)) {
|
|
26
|
+
return localBuild;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Fallback path in user home (where our Rust downloader might place it)
|
|
30
|
+
const homeDir = os.homedir();
|
|
31
|
+
const installPath = path.join(homeDir, '.xpose', 'bin', binName);
|
|
32
|
+
if (fs.existsSync(installPath)) {
|
|
33
|
+
return installPath;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const binPath = getBinaryPath();
|
|
40
|
+
|
|
41
|
+
if (!binPath) {
|
|
42
|
+
console.error('\x1b[31mError:\x1b[0m xpose binary not found.');
|
|
43
|
+
console.log('\nPlease build the project using \x1b[33mcargo build --release\x1b[0m');
|
|
44
|
+
console.log('Or visit the project repository to download a pre-built binary.');
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Delegate everything to the Rust binary
|
|
49
|
+
const args = process.argv.slice(2);
|
|
50
|
+
const child = spawn(binPath, args, {
|
|
51
|
+
stdio: 'inherit',
|
|
52
|
+
env: {
|
|
53
|
+
...process.env,
|
|
54
|
+
// Ensure color support is passed through if needed
|
|
55
|
+
COLORTERM: 'truecolor'
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
child.on('exit', (code) => {
|
|
60
|
+
process.exit(code || 0);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
child.on('error', (err) => {
|
|
64
|
+
console.error('\x1b[31mFailed to start xpose:\x1b[0m', err.message);
|
|
65
|
+
process.exit(1);
|
|
66
|
+
});
|
package/bin/install.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
const https = require('https');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* xpose binary downloader
|
|
10
|
+
* Fetches the pre-compiled Rust binary for the current platform.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const VERSION = '0.1.0';
|
|
14
|
+
const REPO = 'user/xpose-cli'; // Placeholder
|
|
15
|
+
const BASE_URL = `https://github.com/${REPO}/releases/download/v${VERSION}`;
|
|
16
|
+
|
|
17
|
+
const BIN_DIR = path.join(os.homedir(), '.xpose', 'bin');
|
|
18
|
+
|
|
19
|
+
function getReleaseName() {
|
|
20
|
+
const platform = os.platform();
|
|
21
|
+
const arch = os.arch();
|
|
22
|
+
|
|
23
|
+
const targets = {
|
|
24
|
+
'linux-x64': 'x86_64-unknown-linux-musl',
|
|
25
|
+
'darwin-x64': 'x86_64-apple-darwin',
|
|
26
|
+
'darwin-arm64': 'aarch64-apple-darwin',
|
|
27
|
+
'win32-x64': 'x86_64-pc-windows-msvc'
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const targetKey = `${platform}-${arch}`;
|
|
31
|
+
const target = targets[targetKey];
|
|
32
|
+
|
|
33
|
+
if (!target) {
|
|
34
|
+
console.error(`Unsupported platform/architecture: ${targetKey}`);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return `xpose-${target}.tar.gz`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function download() {
|
|
42
|
+
if (!fs.existsSync(BIN_DIR)) {
|
|
43
|
+
fs.mkdirSync(BIN_DIR, { recursive: true });
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const releaseName = getReleaseName();
|
|
47
|
+
const url = `${BASE_URL}/${releaseName}`;
|
|
48
|
+
const dest = path.join(BIN_DIR, releaseName);
|
|
49
|
+
|
|
50
|
+
console.log(`Downloading xpose binary from ${url}...`);
|
|
51
|
+
|
|
52
|
+
const file = fs.createWriteStream(dest);
|
|
53
|
+
|
|
54
|
+
https.get(url, (response) => {
|
|
55
|
+
if (response.statusCode !== 200) {
|
|
56
|
+
console.warn(`Binary not yet published to GitHub Releases (${response.statusCode}).`);
|
|
57
|
+
console.log(`Skipping auto-download. You will need to build manually: cargo build --release`);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
response.pipe(file);
|
|
62
|
+
|
|
63
|
+
file.on('finish', () => {
|
|
64
|
+
file.close();
|
|
65
|
+
console.log('Download complete.');
|
|
66
|
+
// Note: In a full implementation, we would extract the tar.gz here.
|
|
67
|
+
// For now, this establishes the flow.
|
|
68
|
+
});
|
|
69
|
+
}).on('error', (err) => {
|
|
70
|
+
console.error(`Download failed: ${err.message}`);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Only download if being installed via NPM (not in local dev)
|
|
75
|
+
if (!process.env.XPOSE_DEV) {
|
|
76
|
+
download();
|
|
77
|
+
} else {
|
|
78
|
+
console.log('XPOSE_DEV detected, skipping download.');
|
|
79
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "xpose-cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Cloudflare Tunnel CLI for developers - Vivid TUI, auto-copy, and smart defaults.",
|
|
5
|
+
"bin": {
|
|
6
|
+
"xpose": "bin/cli.js"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"bin",
|
|
10
|
+
"README.md"
|
|
11
|
+
],
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "https://github.com/user/xpose-cli"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"cloudflare",
|
|
18
|
+
"tunnel",
|
|
19
|
+
"expose",
|
|
20
|
+
"localhost",
|
|
21
|
+
"rust",
|
|
22
|
+
"tui"
|
|
23
|
+
],
|
|
24
|
+
"author": "",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"scripts": {
|
|
27
|
+
"postinstall": "node bin/install.js"
|
|
28
|
+
},
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=16.0.0"
|
|
31
|
+
}
|
|
32
|
+
}
|