windscout 0.6.4 → 0.7.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/README.md +111 -18
- package/dist/cli.js +99 -36
- package/dist/config.js +31 -4
- package/dist/docker.js +2 -2
- package/dist/measure.js +1 -1
- package/dist/probe.js +83 -45
- package/dist/scan.js +36 -7
- package/dist/tui/docker.js +2 -2
- package/dist/tui/picker.js +118 -53
- package/dist/tui/progress.js +14 -8
- package/dist/tui/tables.js +1 -1
- package/dist/tui/terminal.js +21 -0
- package/dist/tui/widgets.js +2 -0
- package/dist/worker.js +2 -2
- package/package.json +4 -2
package/dist/tui/progress.js
CHANGED
|
@@ -7,11 +7,13 @@ import { countryName, flag } from "./country.js";
|
|
|
7
7
|
import { matrix, resultTable } from "./tables.js";
|
|
8
8
|
import { draw, enterScreen, input, leaveScreen, out, repoLink } from "./terminal.js";
|
|
9
9
|
import { bold, cyan, dim, green, red, vw, yellow } from "./text.js";
|
|
10
|
-
import { bar, box, centerIn } from "./widgets.js";
|
|
10
|
+
import { bar, box, centerIn, spin } from "./widgets.js";
|
|
11
11
|
/** Progress bar, current attempt, results so far. tab switches the table, p pauses, q stops (twice: quits).
|
|
12
12
|
* blocking: we turned the firewall on for the scan */
|
|
13
13
|
export function showProgress(scan, blocking) {
|
|
14
|
-
const { protocols, options: { timeout
|
|
14
|
+
const { protocols, options: { timeout } } = scan;
|
|
15
|
+
const speedTest = scan.options.speedTest || Boolean(scan.options.stopAtFirst); // stop at the first measures speed too
|
|
16
|
+
let quitting = false; // q pressed: a second one quits right away
|
|
15
17
|
let view = speedTest ? "ranking" : "availability"; // tab switches
|
|
16
18
|
let frame = 0;
|
|
17
19
|
const render = () => {
|
|
@@ -22,7 +24,7 @@ export function showProgress(scan, blocking) {
|
|
|
22
24
|
const partial = slots.reduce((sum, s) => sum + (s.current ? Math.min(0.95, (now() - s.attemptStart) / attemptSeconds(timeout)) : 0), 0);
|
|
23
25
|
const fraction = (done + (done < total ? partial : 0)) / total;
|
|
24
26
|
const eta = (total - done) * (done ? elapsed / done : attemptSeconds(timeout) / slots.length);
|
|
25
|
-
const spinner =
|
|
27
|
+
const spinner = spin(frame++);
|
|
26
28
|
const ok = sortOk(results);
|
|
27
29
|
// blocked: nothing worked, and something was really tried (a key limit or a port the app doesn't offer tries nothing)
|
|
28
30
|
const failed = finished.filter((c) => !results.some((r) => r.query === c && r.status === "ok")
|
|
@@ -51,9 +53,10 @@ export function showProgress(scan, blocking) {
|
|
|
51
53
|
: [];
|
|
52
54
|
const width = Math.max(minW, table.length ? vw(table[0]) : 0);
|
|
53
55
|
const inner = width - 4;
|
|
54
|
-
const status =
|
|
55
|
-
:
|
|
56
|
-
: ""
|
|
56
|
+
const status = scan.found ? green(" ✓ a connection held up · stopping the other attempts…")
|
|
57
|
+
: stopping ? yellow(` stopping after the current attempt${many ? "s" : ""}…`)
|
|
58
|
+
: waiting ? yellow(` paused · VPN disconnected${blocking ? " · firewall blocking internet" : ""} · press p to resume`)
|
|
59
|
+
: "";
|
|
57
60
|
const pausing = paused && !waiting && frame % 8 < 5;
|
|
58
61
|
const slotLine = (s) => {
|
|
59
62
|
if (!s.current)
|
|
@@ -102,12 +105,13 @@ export function showProgress(scan, blocking) {
|
|
|
102
105
|
render();
|
|
103
106
|
}
|
|
104
107
|
else if (key.name === "q" || key.name === "escape" || (key.ctrl && key.name === "c")) {
|
|
105
|
-
if (
|
|
108
|
+
if (quitting) { // second press: bail out now
|
|
106
109
|
leaveScreen();
|
|
107
110
|
console.error(`stopped. the VPN may still be connecting: run '${CLI} disconnect'`);
|
|
108
111
|
await restoreFirewall();
|
|
109
112
|
process.exit(130);
|
|
110
113
|
}
|
|
114
|
+
quitting = true;
|
|
111
115
|
scan.stop();
|
|
112
116
|
render();
|
|
113
117
|
}
|
|
@@ -126,11 +130,13 @@ const clip = (s, n) => (s.length > n ? s.slice(0, Math.max(0, n - 1)) + "…" :
|
|
|
126
130
|
/** A line per attempt. Ctrl-C stops after the current attempt, a second one quits */
|
|
127
131
|
export function logProgress(scan) {
|
|
128
132
|
const many = scan.slots.length > 1; // attempts overlap: a whole line once each is done
|
|
133
|
+
let quitting = false; // Ctrl-C pressed: a second one quits right away
|
|
129
134
|
process.on("SIGINT", async () => {
|
|
130
|
-
if (
|
|
135
|
+
if (quitting) {
|
|
131
136
|
await restoreFirewall();
|
|
132
137
|
process.exit(130);
|
|
133
138
|
}
|
|
139
|
+
quitting = true;
|
|
134
140
|
scan.stop();
|
|
135
141
|
});
|
|
136
142
|
return {
|
package/dist/tui/tables.js
CHANGED
|
@@ -14,7 +14,7 @@ const resultRow = (r, i) => [
|
|
|
14
14
|
r.connectS === null ? "-" : `${fmt(r.connectS, 1)} s`,
|
|
15
15
|
r.latencyMs === null ? "-" : `${fmt(r.latencyMs, 0)} ms`,
|
|
16
16
|
r.speedMbps === null ? "-" : bold(`${fmt(r.speedMbps, 1)} Mbps`),
|
|
17
|
-
r.status === "ok" ? green("✓ ok") :
|
|
17
|
+
r.status === "ok" ? green("✓ ok") : ["key limit", "no such port", "unreliable"].includes(r.status) ? yellow(r.status) : red(r.status),
|
|
18
18
|
];
|
|
19
19
|
/** Numbered attempts, in the given order; the Speed column only when the speed test ran */
|
|
20
20
|
export function resultTable(results, speedTest, options) {
|
package/dist/tui/terminal.js
CHANGED
|
@@ -13,6 +13,8 @@ export const input = new PassThrough();
|
|
|
13
13
|
emitKeypressEvents(input);
|
|
14
14
|
let fullscreen = false;
|
|
15
15
|
let link = null; // where draw() last put the repo link
|
|
16
|
+
let waiting; // drawUntilNext()'s timer
|
|
17
|
+
let redrawing = false; // drawUntilNext()'s own redraw is drawing
|
|
16
18
|
const openUrl = (url) => spawn({ darwin: "open", win32: "explorer" }[process.platform] ?? "xdg-open", [url], { stdio: "ignore", detached: true })
|
|
17
19
|
.on("error", () => { }).unref();
|
|
18
20
|
// SGR mouse report: ESC [ < button ; col ; row (M = press, m = release)
|
|
@@ -40,6 +42,7 @@ export function enterScreen() {
|
|
|
40
42
|
}
|
|
41
43
|
/** Back to the normal screen; drops every key and mouse listener */
|
|
42
44
|
export function leaveScreen() {
|
|
45
|
+
clearInterval(waiting);
|
|
43
46
|
if (!fullscreen)
|
|
44
47
|
return;
|
|
45
48
|
fullscreen = false;
|
|
@@ -51,8 +54,26 @@ export function leaveScreen() {
|
|
|
51
54
|
out.write("\x1b[?1000l\x1b[?1006l\x1b[?25h\x1b[?1049l");
|
|
52
55
|
}
|
|
53
56
|
process.on("exit", leaveScreen);
|
|
57
|
+
/** Calls redraw every 120 ms (for a spinner) while the screen waits for the next one: until another screen draws, or leaveScreen() */
|
|
58
|
+
export function drawUntilNext(redraw) {
|
|
59
|
+
const tick = () => {
|
|
60
|
+
redrawing = true;
|
|
61
|
+
try {
|
|
62
|
+
redraw();
|
|
63
|
+
}
|
|
64
|
+
finally {
|
|
65
|
+
redrawing = false;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
clearInterval(waiting);
|
|
69
|
+
waiting = setInterval(tick, 120);
|
|
70
|
+
waiting.unref(); // never what keeps the process alive
|
|
71
|
+
tick();
|
|
72
|
+
}
|
|
54
73
|
/** Redraws in place, centered: home, each line cleared to its end, then clear below (no flicker). Returns the top-left corner */
|
|
55
74
|
export function draw(lines) {
|
|
75
|
+
if (!redrawing)
|
|
76
|
+
clearInterval(waiting); // another screen: it takes over from drawUntilNext()
|
|
56
77
|
const cols = out.columns || 80, rows = out.rows || 24;
|
|
57
78
|
const w = Math.max(...lines.map(vw));
|
|
58
79
|
const left = " ".repeat(Math.max(0, Math.floor((cols - w) / 2)));
|
package/dist/tui/widgets.js
CHANGED
|
@@ -32,6 +32,8 @@ export function grid(cols, rows, { caption, captionRight = "", minWidth = 0, str
|
|
|
32
32
|
out.push(row(cols.map((c) => bold(c.title))), rule("├", "┼", "┤"), ...rows.map(row), rule("╰", "┴", "╯"));
|
|
33
33
|
return out;
|
|
34
34
|
}
|
|
35
|
+
/** A spinner's character for this frame */
|
|
36
|
+
export const spin = (frame) => "⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏"[frame % 10];
|
|
35
37
|
/** s, indented to sit centered in width columns */
|
|
36
38
|
export const centerIn = (s, width) => " ".repeat(Math.max(0, Math.floor((width - vw(s)) / 2))) + s;
|
|
37
39
|
/** Gradient bar (cyan → green) with eighth-block precision on the leading edge */
|
package/dist/worker.js
CHANGED
|
@@ -55,9 +55,9 @@ export async function worker() {
|
|
|
55
55
|
if (!m.probe)
|
|
56
56
|
continue;
|
|
57
57
|
stop = false;
|
|
58
|
-
const { query, protocol, timeout, speedTest } = m.probe;
|
|
58
|
+
const { query, protocol, timeout, speedTest, steady } = m.probe;
|
|
59
59
|
const result = await probe(query, protocol, {
|
|
60
|
-
timeout, speedTest,
|
|
60
|
+
timeout, speedTest, steady,
|
|
61
61
|
stopped: () => stop,
|
|
62
62
|
onStep: (step) => send({ step }),
|
|
63
63
|
measure: async (fn) => {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "windscout",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Find which Windscribe locations and protocols work from your network, ranked by
|
|
3
|
+
"version": "0.7.0",
|
|
4
|
+
"description": "Find which Windscribe locations and protocols work from your network, ranked by speed (unofficial)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"windscout": "dist/cli.js"
|
|
@@ -25,12 +25,14 @@
|
|
|
25
25
|
"keywords": [
|
|
26
26
|
"windscribe",
|
|
27
27
|
"vpn",
|
|
28
|
+
"speedtest",
|
|
28
29
|
"wireguard",
|
|
29
30
|
"stealth",
|
|
30
31
|
"cli",
|
|
31
32
|
"censorship",
|
|
32
33
|
"firewall",
|
|
33
34
|
"unblock",
|
|
35
|
+
"vpn-speed-test",
|
|
34
36
|
"server-finder",
|
|
35
37
|
"docker"
|
|
36
38
|
],
|