windscout 0.4.2 → 0.4.4
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 +1 -1
- package/package.json +1 -1
- package/windscout.js +20 -4
package/README.md
CHANGED
|
@@ -98,7 +98,7 @@ Both modes show an **availability grid**: one row per country, and ✓ / ✗ for
|
|
|
98
98
|
╰─────────────────────────┴────────────────────────┴─────────┴───────────┴─────╯
|
|
99
99
|
```
|
|
100
100
|
|
|
101
|
-
While scanning, press `tab` to switch between the availability grid and the speed or latency ranking. In connect-only mode the scan starts on the availability grid.
|
|
101
|
+
While scanning, press `tab` to switch between the availability grid and the speed or latency ranking. Press `p` (or `space`) to pause: the current attempt finishes, the VPN disconnects, and the scan waits until you press `p` again. Paused time doesn't count toward elapsed or remaining. In connect-only mode the scan starts on the availability grid.
|
|
102
102
|
|
|
103
103
|
## Output
|
|
104
104
|
|
package/package.json
CHANGED
package/windscout.js
CHANGED
|
@@ -150,6 +150,9 @@ async function probe(query, protocol, timeout, onStep = () => {}) {
|
|
|
150
150
|
} finally {
|
|
151
151
|
onStep("disconnecting");
|
|
152
152
|
await cli("disconnect");
|
|
153
|
+
// the CLI's disconnect can return while the app is still tearing down (it does after a timed-out connect);
|
|
154
|
+
// a connect sent then is swallowed and the next attempt reads as an instant "failed", so wait it out
|
|
155
|
+
for (let i = 0; i < 30 && (await state()) === "Disconnecting"; i++) await sleep(1000);
|
|
153
156
|
}
|
|
154
157
|
}
|
|
155
158
|
|
|
@@ -596,12 +599,14 @@ async function scan(countries, protocols, timeout, tty) {
|
|
|
596
599
|
const started = now();
|
|
597
600
|
let done = 0, attemptStart = now(), current = "", step = "", frame = 0, finishedCountries = [];
|
|
598
601
|
let view = speedTest ? "ranking" : "availability"; // tab switches
|
|
602
|
+
// pause lands between attempts, so the VPN is disconnected while waiting; idle = seconds spent paused
|
|
603
|
+
let paused = false, waiting = false, waitStart = 0, idle = 0, resume = () => {};
|
|
599
604
|
|
|
600
605
|
const render = () => {
|
|
601
606
|
const rows = out.rows || 24;
|
|
602
|
-
const elapsed = now() - started;
|
|
607
|
+
const elapsed = now() - started - idle - (waiting ? now() - waitStart : 0);
|
|
603
608
|
// smooth bar: count the running attempt as partially done
|
|
604
|
-
const partial = Math.min(0.95, (now() - attemptStart) / attemptSeconds(timeout));
|
|
609
|
+
const partial = waiting ? 0 : Math.min(0.95, (now() - attemptStart) / attemptSeconds(timeout));
|
|
605
610
|
const fraction = (done + (done < total ? partial : 0)) / total;
|
|
606
611
|
const eta = (total - done) * (done ? elapsed / done : attemptSeconds(timeout));
|
|
607
612
|
const spinner = "⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏"[frame++ % 10];
|
|
@@ -637,8 +642,9 @@ async function scan(countries, protocols, timeout, tty) {
|
|
|
637
642
|
"",
|
|
638
643
|
stop
|
|
639
644
|
? yellow(" stopping after the current attempt…")
|
|
645
|
+
: waiting ? yellow(" paused · VPN disconnected · press p to resume")
|
|
640
646
|
: ` ${cyan(spinner)} ${bold(current)}`,
|
|
641
|
-
stop ? "" : ` ${dim(step)} ${dim("·")} ${duration(now() - attemptStart)}`,
|
|
647
|
+
stop || waiting ? "" : ` ${dim(step)} ${dim("·")} ${duration(now() - attemptStart)}${paused ? yellow(" · pausing after this attempt") : ""}`,
|
|
642
648
|
null,
|
|
643
649
|
centerIn(stats, inner),
|
|
644
650
|
], {
|
|
@@ -655,7 +661,7 @@ async function scan(countries, protocols, timeout, tty) {
|
|
|
655
661
|
while (vw(txt) > width && failed.length) txt = txt.slice(0, -8) + "…"; // ponytail: crude trim, fine for a status line
|
|
656
662
|
lines.push("", centerIn(txt, width));
|
|
657
663
|
}
|
|
658
|
-
lines.push("", centerIn(dim(`tab ${view === "ranking" ? "availability" : "ranking"} view · q stop · q twice force quit`), width), "", centerIn(dim(repoLink), width));
|
|
664
|
+
lines.push("", centerIn(dim(`tab ${view === "ranking" ? "availability" : "ranking"} view · p ${paused ? "resume" : "pause"} · q stop · q twice force quit`), width), "", centerIn(dim(repoLink), width));
|
|
659
665
|
draw(lines);
|
|
660
666
|
};
|
|
661
667
|
|
|
@@ -666,12 +672,17 @@ async function scan(countries, protocols, timeout, tty) {
|
|
|
666
672
|
if (key.name === "tab") {
|
|
667
673
|
view = view === "ranking" ? "availability" : "ranking";
|
|
668
674
|
render();
|
|
675
|
+
} else if (key.name === "p" || key.name === "space") {
|
|
676
|
+
paused = !paused;
|
|
677
|
+
if (!paused) resume();
|
|
678
|
+
render();
|
|
669
679
|
} else if (key.name === "q" || key.name === "escape" || (key.ctrl && key.name === "c")) {
|
|
670
680
|
if (stop++) { // second press: bail out now
|
|
671
681
|
leaveScreen();
|
|
672
682
|
console.error(`stopped. the VPN may still be connecting: run '${CLI} disconnect'`);
|
|
673
683
|
process.exit(130);
|
|
674
684
|
}
|
|
685
|
+
resume(); // a paused scan stops right away
|
|
675
686
|
render();
|
|
676
687
|
}
|
|
677
688
|
});
|
|
@@ -683,6 +694,11 @@ async function scan(countries, protocols, timeout, tty) {
|
|
|
683
694
|
|
|
684
695
|
outer: for (const loc of countries) {
|
|
685
696
|
for (const proto of protocols) {
|
|
697
|
+
if (paused && !stop) {
|
|
698
|
+
waiting = true, waitStart = now();
|
|
699
|
+
await new Promise((r) => (resume = r));
|
|
700
|
+
waiting = false, idle += now() - waitStart;
|
|
701
|
+
}
|
|
686
702
|
if (stop) break outer;
|
|
687
703
|
current = `${flag(loc)} ${countryName(loc)} ${dim("·")} ${proto}`;
|
|
688
704
|
attemptStart = now();
|