polaris-vpn 1.2.1 → 1.2.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.
- package/CHANGELOG.md +15 -0
- package/README.md +162 -75
- package/package.json +1 -1
- package/src/commands/dashboard.js +35 -5
- package/src/commands/tui.js +117 -63
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,21 @@ 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.2.2] - 2026-07-22
|
|
9
|
+
|
|
10
|
+
### Security & Fixed
|
|
11
|
+
|
|
12
|
+
- **TUI Accessibility & Recovery**: Added `renderErrorView` in-app error boundary and emergency navigation shortcuts (`[Esc]`, `[h]`, `[m]`, `[1]`) so TUI view failures or offline states never trap the user or crash the process.
|
|
13
|
+
- **Dependency Security Cleanup**: Replaced unmaintained `qrcode-terminal` with modern, actively maintained `qrcode` library to eliminate supply chain risks.
|
|
14
|
+
- **Vulnerability Patches**: Updated `fast-uri` dependency to resolve host confusion vulnerabilities (`GHSA-v2hh-gcrm-f6hx` / `GHSA-4c8g-83qw-93j6`), bringing `npm audit` to 0 vulnerabilities.
|
|
15
|
+
|
|
16
|
+
## [1.2.1] - 2026-07-22
|
|
17
|
+
|
|
18
|
+
### Security & Fixed
|
|
19
|
+
|
|
20
|
+
- **Dependency Security Cleanup**: Replaced unmaintained `qrcode-terminal` with modern, actively maintained `qrcode` library to eliminate supply chain risks.
|
|
21
|
+
- **Vulnerability Patches**: Updated `fast-uri` dependency to resolve host confusion vulnerabilities (`GHSA-v2hh-gcrm-f6hx` / `GHSA-4c8g-83qw-93j6`), bringing `npm audit` to 0 vulnerabilities.
|
|
22
|
+
|
|
8
23
|
## [1.2.0] - 2026-07-22
|
|
9
24
|
|
|
10
25
|
### Added
|
package/README.md
CHANGED
|
@@ -1,112 +1,199 @@
|
|
|
1
|
-
|
|
1
|
+
<h1 align="center">Polaris VPN</h1>
|
|
2
2
|
|
|
3
|
+
<p align="center">
|
|
4
|
+
<b>Command Your Digital Privacy. A Production-Grade Self-Hosted VPN CLI & Terminal UI.</b>
|
|
5
|
+
</p>
|
|
3
6
|
|
|
4
|
-
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="https://www.npmjs.com/package/polaris-vpn"><img src="https://img.shields.io/npm/v/polaris-vpn?style=for-the-badge&logo=npm&logoColor=white&color=88c0d0" alt="NPM Version" /></a>
|
|
9
|
+
<a href="https://www.npmjs.com/package/polaris-vpn"><img src="https://img.shields.io/npm/dt/polaris-vpn?style=for-the-badge&logo=npm&logoColor=white&color=a3be8c" alt="Total Downloads" /></a>
|
|
10
|
+
<a href="https://www.npmjs.com/package/polaris-vpn"><img src="https://img.shields.io/npm/dm/polaris-vpn?style=for-the-badge&logo=npm&logoColor=white&color=ebcb8b" alt="Monthly Downloads" /></a>
|
|
11
|
+
<a href="https://github.com/Divyo/polaris-vpn"><img src="https://img.shields.io/github/stars/Divyo/polaris-vpn?style=for-the-badge&logo=github&logoColor=white&color=bf616a" alt="GitHub Stars" /></a>
|
|
12
|
+
<a href="https://github.com/Divyo/polaris-vpn/blob/main/LICENSE"><img src="https://img.shields.io/badge/License-Apache%202.0-5e81ac?style=for-the-badge&logo=apache&logoColor=white" alt="License" /></a>
|
|
13
|
+
</p>
|
|
5
14
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
15
|
+
<p align="center">
|
|
16
|
+
<img src="https://img.shields.io/badge/Security_Audit-0_Vulnerabilities-a3be8c?style=for-the-badge&logo=shieldsdotio&logoColor=white" alt="Security Audit" />
|
|
17
|
+
<img src="https://img.shields.io/badge/Node.js-%3E%3D_18.0.0-339933?style=for-the-badge&logo=nodedotjs&logoColor=white" alt="Node Version" />
|
|
18
|
+
<img src="https://img.shields.io/badge/Platforms-macOS_%7C_Linux_%7C_Windows-0078D6?style=for-the-badge&logo=apple&logoColor=white" alt="Platforms" />
|
|
19
|
+
</p>
|
|
10
20
|
|
|
11
|
-
|
|
12
|
-
Interface (TUI).
|
|
21
|
+
<br />
|
|
13
22
|
|
|
14
|
-
|
|
23
|
+
Polaris VPN is a self-hosted VPN manager and Terminal User Interface (TUI) engineered for developers, privacy enthusiasts, and sysadmins. Provision an enterprise-grade WireGuard or Stealth AmneziaWG VPN server on any fresh Linux cloud server in under 60 seconds.
|
|
15
24
|
|
|
16
25
|
---
|
|
17
26
|
|
|
18
|
-
##
|
|
27
|
+
## Technical Performance & Metrics
|
|
28
|
+
|
|
29
|
+
<p align="center">
|
|
30
|
+
<img src="https://img.shields.io/badge/Provisioning_Speed-%3C_60_Seconds-88c0d0?style=flat-square&logo=fastly&logoColor=white" alt="Deploy Time" />
|
|
31
|
+
<img src="https://img.shields.io/badge/Protocols-WireGuard_%7C_AmneziaWG_%7C_TLS_%7C_SSH-b48ead?style=flat-square&logo=wireguard&logoColor=white" alt="Protocols" />
|
|
32
|
+
<img src="https://img.shields.io/badge/DNS_Leak_Protection-Auto--DoH_127.0.0.1%3A5354-a3be8c?style=flat-square&logo=cloudflare&logoColor=white" alt="DNS Protection" />
|
|
33
|
+
<img src="https://img.shields.io/badge/Telemetry-0_Logs_%7C_0_Tracking-bf616a?style=flat-square&logo=gnupg&logoColor=white" alt="Zero Telemetry" />
|
|
34
|
+
<img src="https://img.shields.io/badge/Infrastructure-Oracle_Always_Free_Tier-ebcb8b?style=flat-square&logo=oracle&logoColor=white" alt="Infrastructure" />
|
|
35
|
+
</p>
|
|
36
|
+
|
|
37
|
+
| Performance Metric | Benchmark Standard | Polaris Architecture Implementation |
|
|
38
|
+
| :--- | :--- | :--- |
|
|
39
|
+
| **Server Setup Time** | `< 60 Seconds` | Automated automated Ansible-free shell orchestration |
|
|
40
|
+
| **DNS Leak Rating** | `0% Leak Ratio` | Local DNS-over-HTTPS (DoH) resolver bound to `127.0.0.1:5354` |
|
|
41
|
+
| **Protocol Redundancy** | `4 Tier Fallback` | Automatic protocol failover: `WireGuard` -> `AmneziaWG` -> `TLS` -> `SSH` |
|
|
42
|
+
| **Security Surface** | `0 Vulnerabilities` | Clean security audit, dependency graph lockdown, no native exploits |
|
|
43
|
+
| **Split Tunneling** | `Sub-millisecond` | Kernel-level routing & domain bypass rules via `polaris bypass` |
|
|
44
|
+
| **Mobile Access** | `Instant Sync` | Terminal-rendered vector QR codes via `polaris peer qr` |
|
|
19
45
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Core Capabilities
|
|
49
|
+
|
|
50
|
+
### Automated VPS Provisioning
|
|
51
|
+
Deploy an isolated VPN node on Ubuntu, Debian, or Fedora Linux servers with a single command. Handles WireGuard kernel modules, iptables firewall, and Unbound zero-log DNS configuration automatically.
|
|
52
|
+
|
|
53
|
+
### Live Terminal Dashboard (TUI)
|
|
54
|
+
Full-screen interactive terminal interface built with keyboard navigation (`Nord` design system). Provides live Rx/Tx bandwidth graphs, latency benchmarks, and active peer management.
|
|
55
|
+
|
|
56
|
+
### Auto-DoH & DNS Leak Safeguard
|
|
57
|
+
Binds system DNS on macOS and Linux to a local DoH resolver (`127.0.0.1:5354`) upon tunnel activation. Ensures DNS requests never leak to local ISPs and automatically backs up/restores original system resolver configurations upon teardown.
|
|
58
|
+
|
|
59
|
+
### Deep Packet Inspection (DPI) Stealth Mode
|
|
60
|
+
Integrates AmneziaWG obfuscation parameters (`Jc`, `Jmin`, `Jmax`, `S1`, `S2`, `H1`-`H4`) to randomize packet headers and payload sizes, enabling operation across restricted networks and DPI firewalls.
|
|
61
|
+
|
|
62
|
+
### Server Latency Benchmark & Auto-Selection
|
|
63
|
+
Measure ICMP ping and TCP handshake response times across saved profiles. The `--fastest` flag benchmarks endpoints and connects to the lowest-latency node automatically.
|
|
64
|
+
|
|
65
|
+
---
|
|
32
66
|
|
|
33
|
-
##
|
|
67
|
+
## Installation
|
|
68
|
+
|
|
69
|
+
Install globally via npm (Node.js 18.0.0 or higher required):
|
|
34
70
|
|
|
35
71
|
```bash
|
|
36
72
|
npm install -g polaris-vpn
|
|
37
73
|
```
|
|
38
74
|
|
|
39
|
-
|
|
75
|
+
Verify installation:
|
|
40
76
|
|
|
41
|
-
|
|
77
|
+
```bash
|
|
78
|
+
polaris --version
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
---
|
|
42
82
|
|
|
43
|
-
|
|
83
|
+
## Quick Start Guide
|
|
84
|
+
|
|
85
|
+
### 1. Provision a Server
|
|
86
|
+
Deploy WireGuard or Stealth AmneziaWG on a fresh cloud instance:
|
|
44
87
|
|
|
45
88
|
```bash
|
|
46
|
-
# 1. Provision the server automatically
|
|
47
89
|
polaris deploy --server root@1.2.3.4 --mode amneziawg
|
|
90
|
+
```
|
|
48
91
|
|
|
49
|
-
|
|
92
|
+
### 2. Connect to Your Server
|
|
93
|
+
Connect with automatic DNS leak protection and DoH resolver initialization:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
50
96
|
polaris start --server root@1.2.3.4
|
|
51
97
|
```
|
|
52
98
|
|
|
53
|
-
|
|
54
|
-
|
|
99
|
+
### 3. Save & Auto-Select Lowest Latency Node
|
|
100
|
+
Save server profiles for instant zero-arg connection or lowest-latency routing:
|
|
55
101
|
|
|
56
|
-
|
|
102
|
+
```bash
|
|
103
|
+
polaris add my-server --server root@1.2.3.4
|
|
104
|
+
polaris start --fastest
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Master Terminal Dashboard
|
|
57
110
|
|
|
58
|
-
|
|
111
|
+
Launch the interactive Terminal User Interface:
|
|
59
112
|
|
|
60
113
|
```bash
|
|
61
114
|
polaris
|
|
62
115
|
```
|
|
63
116
|
|
|
64
|
-
|
|
117
|
+
<pre>
|
|
118
|
+
+-----------------------------------------------------------------------------+
|
|
119
|
+
| POLARIS VPN v1.2.2 Command your digital privacy. |
|
|
120
|
+
+--------------------------+--------------------------------------------------+
|
|
121
|
+
| (o) Home | (o) Tunnel Status [*] ACTIVE |
|
|
122
|
+
| (s) Servers | ------------------------------------------------ |
|
|
123
|
+
| (>) Quick Connect | Server ubuntu@1.2.3.4 |
|
|
124
|
+
| (m) Live Monitor | Mode AMNEZIAWG |
|
|
125
|
+
| ----------------------- | Latency 24 ms |
|
|
126
|
+
| (p) Peers | Data v 142.5 MB |
|
|
127
|
+
| (c) Privacy Check | Data ^ 18.2 MB |
|
|
128
|
+
| (d) Deploy VPS | ------------------------------------------------ |
|
|
129
|
+
| ----------------------- | [^/v] Navigate [Enter] Select [Esc/h] Home |
|
|
130
|
+
+--------------------------+--------------------------------------------------+
|
|
131
|
+
</pre>
|
|
132
|
+
|
|
133
|
+
### Navigation Shortcuts
|
|
134
|
+
|
|
135
|
+
| Key Binding | Action |
|
|
136
|
+
| :--- | :--- |
|
|
137
|
+
| `↑ / ↓` or `k / j` | Navigate sidebar items and server lists |
|
|
138
|
+
| `Enter` | Activate highlighted menu item or confirm connection |
|
|
139
|
+
| `Esc` or `h` or `m` | Return to Main Menu / Home from any view or error screen |
|
|
140
|
+
| `b` | Launch live server latency benchmark rankings |
|
|
141
|
+
| `r` | Refresh WireGuard peer data |
|
|
142
|
+
| `1 - 7` | Direct numerical jump to main views |
|
|
143
|
+
| `q` or `Ctrl+C` | Quit Polaris VPN |
|
|
65
144
|
|
|
66
|
-
|
|
67
|
-
- **Live Monitor**: Watch your real-time Rx/Tx bandwidth graphs and server latency.
|
|
68
|
-
- **Peers**: Add or revoke VPN access for friends, or generate a QR code for your phone.
|
|
69
|
-
- **Settings**: Manage your saved server profiles.
|
|
70
|
-
- **Keyboard Shortcuts**: Press `b` in the TUI to view live server latency benchmark rankings.
|
|
145
|
+
---
|
|
71
146
|
|
|
72
|
-
##
|
|
147
|
+
## Complete Command Matrix
|
|
148
|
+
|
|
149
|
+
| Command | Category | Functional Description |
|
|
150
|
+
| :--- | :--- | :--- |
|
|
151
|
+
| `polaris deploy` | Server | Provision Linux VPS with WireGuard/AmneziaWG in under 60 seconds |
|
|
152
|
+
| `polaris start` | Connection | Establish VPN tunnel with Auto-DoH DNS leak protection |
|
|
153
|
+
| `polaris start --fastest` | Connection | Benchmark saved endpoints and connect to lowest latency node |
|
|
154
|
+
| `polaris start --failover` | Connection | Connect with automatic multi-protocol fallback |
|
|
155
|
+
| `polaris stop` | Connection | Disconnect tunnel and restore original system DNS settings |
|
|
156
|
+
| `polaris benchmark` | Diagnostics | Rank saved server profiles by ping and TCP handshake speed |
|
|
157
|
+
| `polaris bypass add <target>` | Network | Add domain or IP subnet to split-tunneling bypass rules |
|
|
158
|
+
| `polaris bypass list` | Network | Display active split-tunneling bypass rules |
|
|
159
|
+
| `polaris import <file.conf>` | Profile | Import WireGuard or AmneziaWG `.conf` file |
|
|
160
|
+
| `polaris export <alias>` | Profile | Export profile config or display terminal QR code |
|
|
161
|
+
| `polaris dashboard` | Interface | Launch interactive Live Monitor TUI |
|
|
162
|
+
| `polaris status --full` | Status | Output connection state, GeoIP location, and rx/tx transfer |
|
|
163
|
+
| `polaris peer add <name>` | Access | Provision new peer config for additional devices |
|
|
164
|
+
| `polaris peer qr <name>` | Access | Render vector QR code in terminal for mobile setup |
|
|
165
|
+
| `polaris check` | Privacy | Run 3-point privacy audit (IP, DNS leak, IPv6 exposure) |
|
|
166
|
+
|
|
167
|
+
*(All commands support the `--json` flag for integration scripts and CI/CD pipelines)*
|
|
73
168
|
|
|
74
|
-
|
|
169
|
+
---
|
|
75
170
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
- **ARM VM (A1.Flex)**: Up to 4 OCPUs and 24 GB RAM — completely free forever.
|
|
101
|
-
- **10 TB Bandwidth**: More than enough for high-speed streaming and gaming.
|
|
102
|
-
- No third-party VPN company logging your data or selling your traffic. You are in total command.
|
|
103
|
-
|
|
104
|
-
## 🔒 Trust Model
|
|
105
|
-
|
|
106
|
-
- **You own the server**: No reliance on third-party commercial VPN providers.
|
|
107
|
-
- **Zero telemetry**: No tracking, no crash reporting, no analytics.
|
|
108
|
-
- **Auto-DoH & Local Kill-Switch**: Traffic is strictly encrypted and locked to the VPN interface to prevent IP and DNS leaks.
|
|
109
|
-
- **Open Source**: Apache 2.0 licensed, fully auditable codebase.
|
|
171
|
+
## Infrastructure: Oracle Cloud Always Free Tier
|
|
172
|
+
|
|
173
|
+
Polaris VPN is optimized for the **Oracle Cloud Always Free Tier**:
|
|
174
|
+
|
|
175
|
+
<p align="center">
|
|
176
|
+
<img src="https://img.shields.io/badge/Instance-Ampere_A1_Compute-000000?style=flat-square&logo=oracle&logoColor=white" />
|
|
177
|
+
<img src="https://img.shields.io/badge/Resources-4_OCPU_%7C_24_GB_RAM-88c0d0?style=flat-square&logo=cpu&logoColor=white" />
|
|
178
|
+
<img src="https://img.shields.io/badge/Bandwidth-10_TB_/_Month-a3be8c?style=flat-square&logo=datacamp&logoColor=white" />
|
|
179
|
+
</p>
|
|
180
|
+
|
|
181
|
+
- **Zero Cost**: 4 OCPUs and 24 GB RAM free forever.
|
|
182
|
+
- **High Throughput**: 10 TB outbound data transfer per month.
|
|
183
|
+
- **Total Ownership**: No commercial VPN providers logging or monetizing your traffic.
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## Trust Architecture & Security Posture
|
|
188
|
+
|
|
189
|
+
- **Zero Telemetry**: No tracking, no crash reports, no central authentication servers.
|
|
190
|
+
- **Local Key Generation**: Cryptographic key pairs generated locally via OS entropy source.
|
|
191
|
+
- **Native OS Kill-Switch**: Binds firewall rules (`pf` on macOS, `iptables` on Linux) to block unencrypted leak vectors.
|
|
192
|
+
- **Audit Verification**: Clean dependency graph with zero vulnerability alerts (`npm audit` verified).
|
|
193
|
+
- **Open Source**: Licensed under Apache 2.0.
|
|
110
194
|
|
|
111
195
|
---
|
|
112
|
-
|
|
196
|
+
|
|
197
|
+
<p align="center">
|
|
198
|
+
<i>Engineered for total digital sovereignty.</i>
|
|
199
|
+
</p>
|
package/package.json
CHANGED
|
@@ -102,6 +102,28 @@ export default async () => {
|
|
|
102
102
|
|
|
103
103
|
if (!info) {
|
|
104
104
|
console.error('Tunnel is down. Start the tunnel first to use the dashboard.');
|
|
105
|
+
console.log('\x1b[36mPress any key (or Esc/h) to open the Polaris TUI main menu, or Ctrl+C to exit...\x1b[0m');
|
|
106
|
+
try {
|
|
107
|
+
await new Promise(resolve => {
|
|
108
|
+
if (process.stdin.isTTY) {
|
|
109
|
+
process.stdin.setRawMode(true);
|
|
110
|
+
process.stdin.resume();
|
|
111
|
+
const onData = () => {
|
|
112
|
+
try {
|
|
113
|
+
process.stdin.removeListener('data', onData);
|
|
114
|
+
process.stdin.setRawMode(false);
|
|
115
|
+
process.stdin.pause();
|
|
116
|
+
} catch (e) {}
|
|
117
|
+
resolve();
|
|
118
|
+
};
|
|
119
|
+
process.stdin.on('data', onData);
|
|
120
|
+
} else {
|
|
121
|
+
resolve();
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
const m = await import('./tui.js');
|
|
125
|
+
await m.default();
|
|
126
|
+
} catch (e) {}
|
|
105
127
|
return;
|
|
106
128
|
}
|
|
107
129
|
|
|
@@ -156,7 +178,7 @@ export default async () => {
|
|
|
156
178
|
|
|
157
179
|
// 5. Hotkeys Legend
|
|
158
180
|
grid.set(11, 7, 1, 5, blessed.box, {
|
|
159
|
-
content: ` ${t(D.accent, b('[↑/↓]'))} ${t(D.muted, 'Scroll')} ${t(D.accent, b('[Esc/q]'))} ${t(D.muted, 'Back to Home')}`,
|
|
181
|
+
content: ` ${t(D.accent, b('[↑/↓]'))} ${t(D.muted, 'Scroll')} ${t(D.accent, b('[Esc/h/m/q]'))} ${t(D.muted, 'Back to Home')}`,
|
|
160
182
|
tags: true,
|
|
161
183
|
style: { bg: D.bg, fg: D.text }
|
|
162
184
|
});
|
|
@@ -186,20 +208,27 @@ export default async () => {
|
|
|
186
208
|
const exitDashboard = async () => {
|
|
187
209
|
if (isClosing) return;
|
|
188
210
|
isClosing = true;
|
|
189
|
-
|
|
211
|
+
try {
|
|
212
|
+
screen.destroy();
|
|
213
|
+
} catch (e) {}
|
|
190
214
|
|
|
191
215
|
// Re-import and launch the master TUI
|
|
192
|
-
|
|
193
|
-
|
|
216
|
+
try {
|
|
217
|
+
const m = await import('./tui.js');
|
|
218
|
+
await m.default();
|
|
219
|
+
} catch (e) {
|
|
220
|
+
process.exit(0);
|
|
221
|
+
}
|
|
194
222
|
};
|
|
195
223
|
|
|
196
|
-
screen.key(['escape', 'q', 'C-c', 'h'], exitDashboard);
|
|
224
|
+
screen.key(['escape', 'q', 'C-c', 'h', 'm', '1', 'backspace'], exitDashboard);
|
|
197
225
|
|
|
198
226
|
peersTable.focus();
|
|
199
227
|
|
|
200
228
|
// ─── UPDATER ────────────────────────────────────────────────────────────
|
|
201
229
|
const updateDashboard = async () => {
|
|
202
230
|
if (isClosing) return;
|
|
231
|
+
try {
|
|
203
232
|
|
|
204
233
|
// 1. Fetch IP & GeoIP (runs once)
|
|
205
234
|
if (!geoFetched) {
|
|
@@ -269,6 +298,7 @@ export default async () => {
|
|
|
269
298
|
}
|
|
270
299
|
|
|
271
300
|
screen.render();
|
|
301
|
+
} catch (err) {}
|
|
272
302
|
};
|
|
273
303
|
|
|
274
304
|
const timer = setInterval(updateDashboard, 1000);
|
package/src/commands/tui.js
CHANGED
|
@@ -462,12 +462,28 @@ export default async () => {
|
|
|
462
462
|
[['1','Home'],['2','Servers'],['3','Quick Connect'],
|
|
463
463
|
['4','Live Monitor'],['5','Peers'],['6','Privacy Check'],['7','Deploy VPS']]
|
|
464
464
|
.forEach(([k, v]) => L.push(` ${t(D.accent, k.padEnd(16))} ${mu(v)}`));
|
|
465
|
-
setView('help', L, ['?', 'Close'], ['Esc', '
|
|
465
|
+
setView('help', L, ['?', 'Close'], ['Esc / h', 'Main Menu']);
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
// ─── ERROR VIEW RENDERER ──────────────────────────────────────────
|
|
469
|
+
const renderErrorView = (title, message) => {
|
|
470
|
+
const L = [];
|
|
471
|
+
L.push(`${t(D.danger, b(`⚠ ${title}`))}`);
|
|
472
|
+
L.push(hr());
|
|
473
|
+
L.push('');
|
|
474
|
+
L.push(` ${t(D.danger, '✖')} ${b(message)}`);
|
|
475
|
+
L.push('');
|
|
476
|
+
L.push(hr());
|
|
477
|
+
L.push('');
|
|
478
|
+
L.push(` ${mu('Press ')}${t(D.accent, '[Esc]')}${mu(', ')}${t(D.accent, '[h]')}${mu(' or ')}${t(D.accent, '[1]')}${mu(' to return to the Main Menu.')}`);
|
|
479
|
+
setView('error', L, ['Esc / h / 1', 'Main Menu']);
|
|
466
480
|
};
|
|
467
481
|
|
|
468
482
|
// ─── SUSPEND + RUN COMMAND ────────────────────────────────────────
|
|
469
483
|
const runCmd = async (fn) => {
|
|
470
|
-
|
|
484
|
+
try {
|
|
485
|
+
screen.destroy();
|
|
486
|
+
} catch (e) {}
|
|
471
487
|
process.stdout.write('\x1b[2J\x1b[H');
|
|
472
488
|
console.log(`\x1b[36m\n Polaris VPN \x1b[0m\x1b[90m— running command...\x1b[0m\n`);
|
|
473
489
|
try {
|
|
@@ -476,65 +492,98 @@ export default async () => {
|
|
|
476
492
|
console.error('\n\x1b[31m Error:\x1b[0m', err.message);
|
|
477
493
|
}
|
|
478
494
|
console.log('\n\x1b[90m ─────────────────────────────────\x1b[0m');
|
|
479
|
-
console.log('\x1b[36m Press any key to return...\x1b[0m');
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
495
|
+
console.log('\x1b[36m Press any key to return to main menu...\x1b[0m');
|
|
496
|
+
try {
|
|
497
|
+
await new Promise(resolve => {
|
|
498
|
+
if (process.stdin.isTTY) {
|
|
499
|
+
process.stdin.setRawMode(true);
|
|
500
|
+
process.stdin.resume();
|
|
501
|
+
const onData = () => {
|
|
502
|
+
try {
|
|
503
|
+
process.stdin.removeListener('data', onData);
|
|
504
|
+
process.stdin.setRawMode(false);
|
|
505
|
+
process.stdin.pause();
|
|
506
|
+
} catch (e) {}
|
|
507
|
+
resolve();
|
|
508
|
+
};
|
|
509
|
+
process.stdin.on('data', onData);
|
|
510
|
+
} else {
|
|
511
|
+
resolve();
|
|
512
|
+
}
|
|
487
513
|
});
|
|
488
|
-
})
|
|
489
|
-
|
|
490
|
-
|
|
514
|
+
} catch (e) {}
|
|
515
|
+
try {
|
|
516
|
+
const m = await import('./tui.js');
|
|
517
|
+
await m.default();
|
|
518
|
+
} catch (err) {
|
|
519
|
+
console.error('Failed to restore TUI:', err.message);
|
|
520
|
+
process.exit(1);
|
|
521
|
+
}
|
|
491
522
|
};
|
|
492
523
|
|
|
493
524
|
// ─── ACTIVATE A VIEW ──────────────────────────────────────────────
|
|
494
525
|
const goto = async (viewId) => {
|
|
495
|
-
|
|
526
|
+
try {
|
|
527
|
+
currentView = viewId;
|
|
496
528
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
529
|
+
// Sync sidebar highlight
|
|
530
|
+
const vi = VIEWS.findIndex(v => v && v.id === viewId);
|
|
531
|
+
const ni = NAV.indexOf(vi);
|
|
532
|
+
if (ni !== -1) menuIdx = ni;
|
|
501
533
|
|
|
502
|
-
|
|
503
|
-
|
|
534
|
+
renderSidebar();
|
|
535
|
+
screen.render();
|
|
504
536
|
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
537
|
+
// Delegated actions
|
|
538
|
+
if (viewId === 'connect') {
|
|
539
|
+
await runCmd(async () => {
|
|
540
|
+
const run = (await import('./start.js')).default;
|
|
541
|
+
await run({ mode: 'auto', json: false });
|
|
542
|
+
});
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
if (viewId === 'dashboard') {
|
|
546
|
+
const { getActiveTunnel } = await import('../core/tunnel-service.js');
|
|
547
|
+
const info = getActiveTunnel();
|
|
548
|
+
if (!info) {
|
|
549
|
+
renderErrorView('Live Monitor Unavailable', 'No active tunnel found. Please connect to a VPN server first before launching the Live Monitor.');
|
|
550
|
+
screen.render();
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
try {
|
|
554
|
+
screen.destroy();
|
|
555
|
+
const m = await import('./dashboard.js');
|
|
556
|
+
await m.default();
|
|
557
|
+
} catch (err) {
|
|
558
|
+
const m = await import('./tui.js');
|
|
559
|
+
await m.default();
|
|
560
|
+
}
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
if (viewId === 'quit') { process.exit(0); }
|
|
564
|
+
|
|
565
|
+
// Render static content views
|
|
566
|
+
switch (viewId) {
|
|
567
|
+
case 'home': renderHome(); break;
|
|
568
|
+
case 'servers': srvIdx = 0; renderServers(); break;
|
|
569
|
+
case 'check': renderCheck(); break;
|
|
570
|
+
case 'deploy': renderDeploy(); break;
|
|
571
|
+
case 'peers': renderPeers(); break;
|
|
572
|
+
case 'disconnect': renderDisconnect(); break;
|
|
573
|
+
case 'help': renderHelp(); break;
|
|
574
|
+
default: renderHome(); break;
|
|
575
|
+
}
|
|
576
|
+
screen.render();
|
|
577
|
+
} catch (err) {
|
|
578
|
+
renderErrorView('Navigation Error', err.message || 'An error occurred while rendering this view.');
|
|
579
|
+
screen.render();
|
|
529
580
|
}
|
|
530
|
-
screen.render();
|
|
531
581
|
};
|
|
532
582
|
|
|
533
583
|
// ─── KEYBOARD: ALL HANDLED HERE, NO BLESSED.LIST FOCUS FIGHTS ────
|
|
534
584
|
screen.key(['q', 'C-c'], () => process.exit(0));
|
|
535
585
|
screen.key(['?'], () => goto(currentView === 'help' ? 'home' : 'help'));
|
|
536
|
-
screen.key(['h'], () => goto('home'));
|
|
537
|
-
screen.key(['1'], () => goto('home'));
|
|
586
|
+
screen.key(['h', 'm', '1'], () => goto('home'));
|
|
538
587
|
screen.key(['2'], () => goto('servers'));
|
|
539
588
|
screen.key(['3'], () => goto('connect'));
|
|
540
589
|
screen.key(['4'], () => goto('dashboard'));
|
|
@@ -644,26 +693,31 @@ export default async () => {
|
|
|
644
693
|
|
|
645
694
|
// ─── RESIZE ───────────────────────────────────────────────────────
|
|
646
695
|
screen.on('resize', () => {
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
696
|
+
try {
|
|
697
|
+
screen.realloc();
|
|
698
|
+
renderSidebar();
|
|
699
|
+
switch (currentView) {
|
|
700
|
+
case 'home': renderHome(); break;
|
|
701
|
+
case 'servers': renderServers(); break;
|
|
702
|
+
case 'check': renderCheck(); break;
|
|
703
|
+
case 'deploy': renderDeploy(); break;
|
|
704
|
+
case 'peers': renderPeers(); break;
|
|
705
|
+
case 'disconnect': renderDisconnect(); break;
|
|
706
|
+
case 'help': renderHelp(); break;
|
|
707
|
+
default: renderHome(); break;
|
|
708
|
+
}
|
|
709
|
+
screen.render();
|
|
710
|
+
} catch (e) {}
|
|
659
711
|
});
|
|
660
712
|
|
|
661
713
|
// ─── AUTO-REFRESH STATUS ──────────────────────────────────────────
|
|
662
714
|
setInterval(() => {
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
715
|
+
try {
|
|
716
|
+
renderSidebar();
|
|
717
|
+
if (currentView === 'home') renderHome();
|
|
718
|
+
if (currentView === 'peers') renderPeers();
|
|
719
|
+
screen.render();
|
|
720
|
+
} catch (e) {}
|
|
667
721
|
}, 5000);
|
|
668
722
|
|
|
669
723
|
// ─── INIT ─────────────────────────────────────────────────────────
|