windscout 0.3.0 → 0.4.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 +1 -0
- package/package.json +1 -1
- package/windscout.js +18 -6
package/README.md
CHANGED
|
@@ -74,6 +74,7 @@ windscout -t 40 -a # longer connect timeout, also list
|
|
|
74
74
|
| `-t, --timeout` | seconds to wait for each connection | 20 |
|
|
75
75
|
| `-a, --all` | include failed attempts in the final table | off |
|
|
76
76
|
| `-n, --no-speed` | connect-only mode: skip the download test, rank by latency | saved, or off |
|
|
77
|
+
| `-v, --version` | print the installed version, and the update command if a newer one is on npm | |
|
|
77
78
|
|
|
78
79
|
## Connect-only mode
|
|
79
80
|
|
package/package.json
CHANGED
package/windscout.js
CHANGED
|
@@ -175,6 +175,8 @@ const updateNotice = () => `update available: ${VERSION} → ${update} · run: $
|
|
|
175
175
|
|
|
176
176
|
const color = (code, s) => (process.env.NO_COLOR || !process.stdout.isTTY ? s : `\x1b[${code}m${s}\x1b[0m`);
|
|
177
177
|
const bold = (s) => color(1, s), dim = (s) => color(2, s), green = (s) => color(32, s);
|
|
178
|
+
// underline off (24) instead of a full reset, so it nests inside dim/bold
|
|
179
|
+
const under = (s) => (process.env.NO_COLOR || !process.stdout.isTTY ? s : `\x1b[4m${s}\x1b[24m`);
|
|
178
180
|
const cyan = (s) => color(36, s), yellow = (s) => color(33, s), red = (s) => color(31, s);
|
|
179
181
|
const fmt = (v, digits) => (v === null || v === undefined ? "-" : v.toFixed(digits));
|
|
180
182
|
const list = (s) => s.split(",").map((x) => x.trim()).filter(Boolean);
|
|
@@ -271,9 +273,12 @@ function matrix(results, protocols, { minWidth = 0, limit = Infinity } = {}) {
|
|
|
271
273
|
const reachable = ranked.filter((c) => c.good).length;
|
|
272
274
|
const rows = ranked.slice(0, limit).map(({ q, rs }) => {
|
|
273
275
|
const seen = rs.find((r) => r.status === "ok") ?? rs.find((r) => r.location);
|
|
276
|
+
// nickname = exact datacenter; protocols can land on different ones, so list each that connected
|
|
277
|
+
const nicks = [...new Set(rs.filter((r) => r.status === "ok").map((r) => r.location.split(" - ")[1]).filter(Boolean))];
|
|
274
278
|
return [
|
|
275
279
|
`${flag(q)} ${countryName(q)}`,
|
|
276
280
|
seen?.location?.split(" - ")[0] || dim("-"),
|
|
281
|
+
nicks.join(", ") || dim("-"),
|
|
277
282
|
...protocols.map((p) => {
|
|
278
283
|
const r = rs.find((x) => x.protocol === p);
|
|
279
284
|
return !r ? dim("·") : r.status === "ok" ? green("✓") : red("✗");
|
|
@@ -281,13 +286,13 @@ function matrix(results, protocols, { minWidth = 0, limit = Infinity } = {}) {
|
|
|
281
286
|
];
|
|
282
287
|
});
|
|
283
288
|
return grid(
|
|
284
|
-
[{ title: "Country" }, { title: "City" }, ...protocols.map((p) => ({ title: p, align: "center" }))],
|
|
289
|
+
[{ title: "Country" }, { title: "City" }, { title: "Location" }, ...protocols.map((p) => ({ title: p, align: "center" }))],
|
|
285
290
|
rows,
|
|
286
291
|
{
|
|
287
292
|
caption: bold("Availability"),
|
|
288
293
|
captionRight: dim(`${reachable}/${ranked.length} countries reachable${ranked.length > limit ? ` · top ${limit}` : ""}`),
|
|
289
294
|
minWidth,
|
|
290
|
-
stretch:
|
|
295
|
+
stretch: 2,
|
|
291
296
|
},
|
|
292
297
|
);
|
|
293
298
|
}
|
|
@@ -409,8 +414,8 @@ function pick(saved, timeout) {
|
|
|
409
414
|
top = Math.max(0, Math.min(top, list.length - height));
|
|
410
415
|
|
|
411
416
|
const protoLine = PROTOCOLS.map((p, i) =>
|
|
412
|
-
`${dim(String(i + 1))} ${protos.has(p) ? green("● " + p) : dim("○ " + p)}`).join(" ");
|
|
413
|
-
const speedLine = `${dim("s")} ${speedTest ? green("● speed test") : dim("○ speed test")} ${dim(speedTest
|
|
417
|
+
`${dim(under(String(i + 1)))} ${protos.has(p) ? green("● " + p) : dim("○ " + p)}`).join(" ");
|
|
418
|
+
const speedLine = `${dim(under("s"))} ${speedTest ? green("● speed test") : dim("○ speed test")} ${dim(speedTest
|
|
414
419
|
? "ranks by download speed · adds ~15s per working connection"
|
|
415
420
|
: "off: connect-only, ranks by latency")}`;
|
|
416
421
|
const search = searching
|
|
@@ -425,7 +430,7 @@ function pick(saved, timeout) {
|
|
|
425
430
|
return [`${here ? cyan("❯") : " "} ${mark}`, here ? bold(code) : dim(code), here ? bold(cyan(name)) : name];
|
|
426
431
|
});
|
|
427
432
|
if (!body.length) body.push([" ", "", dim("no country matches")]);
|
|
428
|
-
const buttons = [["start", "▶ Start"], ["all", query ? "
|
|
433
|
+
const buttons = [["start", "▶ Start"], ["all", under("A") + (query ? "ll shown" : "ll")], ["none", under("N") + (query ? "one shown" : "one")], ["quit", under("Q") + "uit"]];
|
|
429
434
|
const buttonLine = buttons.map(([id, label]) => (id === "start" ? bold(cyan(`[ ${label} ]`)) : dim(`[ ${label} ]`))).join(" ");
|
|
430
435
|
|
|
431
436
|
const table = grid([{ title: "" }, { title: "Code" }, { title: "Country" }], body,
|
|
@@ -754,6 +759,7 @@ Options:
|
|
|
754
759
|
-t, --timeout SEC seconds to wait for each connection (default: 20)
|
|
755
760
|
-a, --all also show failed attempts in the final table
|
|
756
761
|
-n, --no-speed connect-only: skip the download test, rank by latency (faster scan)
|
|
762
|
+
-v, --version show the installed version (and whether a newer one is out)
|
|
757
763
|
-h, --help show this help
|
|
758
764
|
|
|
759
765
|
Selection is stored in ${CONFIG}
|
|
@@ -767,11 +773,17 @@ async function main() {
|
|
|
767
773
|
timeout: { type: "string", short: "t", default: "20" },
|
|
768
774
|
all: { type: "boolean", short: "a", default: false },
|
|
769
775
|
help: { type: "boolean", short: "h", default: false },
|
|
776
|
+
version: { type: "boolean", short: "v", default: false },
|
|
770
777
|
"no-speed": { type: "boolean", short: "n", default: false },
|
|
771
778
|
},
|
|
772
779
|
});
|
|
773
780
|
if (a.help) return console.log(HELP);
|
|
774
781
|
updateCheck = checkUpdate(); // runs while the app starts and the user picks, before the VPN starts switching
|
|
782
|
+
if (a.version) {
|
|
783
|
+
console.log(`windscout ${VERSION}`);
|
|
784
|
+
await updateCheck;
|
|
785
|
+
return update && console.error(yellow(updateNotice()));
|
|
786
|
+
}
|
|
775
787
|
const timeout = Number(a.timeout);
|
|
776
788
|
if (!(timeout > 0)) throw new Error("--timeout must be a positive number");
|
|
777
789
|
|
|
@@ -789,7 +801,7 @@ async function main() {
|
|
|
789
801
|
if (!tty) throw new Error("no terminal for the picker: pass countries with -c");
|
|
790
802
|
const saved = loadConfig();
|
|
791
803
|
if (protocols && saved) saved.protocols = protocols.filter((p) => PROTOCOLS.includes(p));
|
|
792
|
-
speedTest = !a["no-speed"] && (saved?.speed ??
|
|
804
|
+
speedTest = !a["no-speed"] && (saved?.speed ?? false); // first run: connect-only
|
|
793
805
|
const choice = await pick(saved, timeout);
|
|
794
806
|
if (!choice) return;
|
|
795
807
|
countries = choice.countries;
|