@dutodev/cli 0.4.4 → 0.4.5
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/dist/index.js +62 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -106,7 +106,7 @@ Com os túneis no ar, o terminal mostra cada túnel (URL, destino local, requisi
|
|
|
106
106
|
| `tab`, `↑↓` | Alterna entre túneis e requisições e move a seleção |
|
|
107
107
|
| `q` ou Ctrl+C | Fecha os túneis e sai |
|
|
108
108
|
|
|
109
|
-
Se o túnel está no ar mas nada responde na porta local, quem abrir o endereço no navegador vê uma página do Duto que
|
|
109
|
+
Se o túnel está no ar mas nada responde na porta local, quem abrir o endereço no navegador vê uma página do Duto que volta sozinha quando o serviço sobe. Ela espera por uma conexão só (SSE): a CLI checa a porta na sua máquina e avisa a página assim que o serviço responde, sem recarregar de tempos em tempos. Clientes que não são navegador (`curl`, APIs) recebem a mesma resposta em texto. Em ambos os casos o status é `503` com `Retry-After: 3`. Os caminhos `/__duto/…` são reservados para isso e nunca chegam ao seu app.
|
|
110
110
|
|
|
111
111
|
Fora de um terminal interativo (pipe, CI) ou com `--no-ui`, a CLI imprime um log simples. No Windows a tela cheia vem desligada; use `--ui` para tentar.
|
|
112
112
|
|
package/dist/index.js
CHANGED
|
@@ -368,6 +368,24 @@ var esc = (s) => s.replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">":
|
|
|
368
368
|
function wantsHtml(accept) {
|
|
369
369
|
return /\btext\/html\b/i.test(accept ?? "");
|
|
370
370
|
}
|
|
371
|
+
var WAIT_SCRIPT = `
|
|
372
|
+
<script>
|
|
373
|
+
(function () {
|
|
374
|
+
var delay = 3000;
|
|
375
|
+
function poll() {
|
|
376
|
+
setTimeout(function () {
|
|
377
|
+
fetch(location.href, { method: "HEAD", cache: "no-store" })
|
|
378
|
+
.then(function (r) { if (r.status !== 503) location.reload(); else again(); })
|
|
379
|
+
.catch(again);
|
|
380
|
+
}, delay);
|
|
381
|
+
}
|
|
382
|
+
function again() { delay = Math.min(delay * 2, 30000); poll(); }
|
|
383
|
+
if (!window.EventSource) return poll();
|
|
384
|
+
var fails = 0, es = new EventSource("/__duto/espera");
|
|
385
|
+
es.addEventListener("up", function () { es.close(); location.reload(); });
|
|
386
|
+
es.onerror = function () { if (++fails >= 3) { es.close(); poll(); } };
|
|
387
|
+
})();
|
|
388
|
+
</script>`;
|
|
371
389
|
function unavailablePage(o) {
|
|
372
390
|
const tunnel2 = esc(o.tunnel);
|
|
373
391
|
const local = `localhost:${o.port}`;
|
|
@@ -376,7 +394,7 @@ function unavailablePage(o) {
|
|
|
376
394
|
<head>
|
|
377
395
|
<meta charset="utf-8">
|
|
378
396
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
379
|
-
|
|
397
|
+
<meta name="robots" content="noindex">
|
|
380
398
|
<title>Nada respondendo em ${local} \xB7 duto</title>
|
|
381
399
|
<style>
|
|
382
400
|
:root{--bg:#f6f6f3;--fg:#0f1110;--muted:#5f6360;--line:#dcddd8;--card:#fff;--signal:#2dd4bf;--signal-text:#0f766e}
|
|
@@ -400,8 +418,8 @@ h1 code{font-size:.86em;font-weight:600}
|
|
|
400
418
|
<h1>Nada respondendo em <code>${local}</code></h1>
|
|
401
419
|
<p>O t\xFAnel <code>${tunnel2}</code> est\xE1 no ar, mas o servi\xE7o local n\xE3o respondeu. Suba o servidor na m\xE1quina que abriu o t\xFAnel.</p>
|
|
402
420
|
<p>Se voc\xEA s\xF3 recebeu este link, avise quem o enviou.</p>
|
|
403
|
-
<div class="retry">${o.retry ?
|
|
404
|
-
</main
|
|
421
|
+
<div class="retry">${o.retry ? `<b>Aguardando o servi\xE7o.</b> A p\xE1gina volta sozinha quando ele responder.<noscript> Recarregue quando o servi\xE7o estiver no ar.</noscript>` : "Recarregue a p\xE1gina quando o servi\xE7o estiver no ar."}</div>
|
|
422
|
+
</main>${o.retry ? WAIT_SCRIPT : ""}
|
|
405
423
|
</body>
|
|
406
424
|
</html>
|
|
407
425
|
`;
|
|
@@ -485,6 +503,38 @@ function rewriteLocation(location, localHost2, publicHost) {
|
|
|
485
503
|
function hostOf(req) {
|
|
486
504
|
return (req.headers.host ?? "").replace(/:\d+$/, "").toLowerCase();
|
|
487
505
|
}
|
|
506
|
+
var DUTO_PREFIX = "/__duto/";
|
|
507
|
+
var WAIT_PATH = "/__duto/espera";
|
|
508
|
+
var WAIT_MAX_MS = 10 * 6e4;
|
|
509
|
+
async function isUp(port) {
|
|
510
|
+
return await probe("127.0.0.1", port) || await probe("::1", port);
|
|
511
|
+
}
|
|
512
|
+
function waitForService(res, port, checkMs, pingMs) {
|
|
513
|
+
res.writeHead(200, { "content-type": "text/event-stream", "cache-control": "no-store", "x-accel-buffering": "no" });
|
|
514
|
+
res.write(": duto\n\n");
|
|
515
|
+
let busy = false;
|
|
516
|
+
const stop = () => {
|
|
517
|
+
clearInterval(check);
|
|
518
|
+
clearInterval(ping);
|
|
519
|
+
clearTimeout(limit);
|
|
520
|
+
};
|
|
521
|
+
const check = setInterval(async () => {
|
|
522
|
+
if (busy) return;
|
|
523
|
+
busy = true;
|
|
524
|
+
const up = await isUp(port);
|
|
525
|
+
busy = false;
|
|
526
|
+
if (up && !res.writableEnded) {
|
|
527
|
+
stop();
|
|
528
|
+
res.end("event: up\ndata: 1\n\n");
|
|
529
|
+
}
|
|
530
|
+
}, checkMs);
|
|
531
|
+
const ping = setInterval(() => res.write(": ping\n\n"), pingMs);
|
|
532
|
+
const limit = setTimeout(() => {
|
|
533
|
+
stop();
|
|
534
|
+
res.end();
|
|
535
|
+
}, WAIT_MAX_MS);
|
|
536
|
+
res.on("close", stop);
|
|
537
|
+
}
|
|
488
538
|
async function startProxy(o) {
|
|
489
539
|
const max = o.maxBody ?? MAX_BODY;
|
|
490
540
|
let nextId = 1;
|
|
@@ -495,6 +545,12 @@ async function startProxy(o) {
|
|
|
495
545
|
res.end("T\xFAnel n\xE3o encontrado nesta sess\xE3o");
|
|
496
546
|
return;
|
|
497
547
|
}
|
|
548
|
+
if ((req.url ?? "").startsWith(DUTO_PREFIX)) {
|
|
549
|
+
if (req.url === WAIT_PATH && req.method === "GET") return waitForService(res, route.port, o.waitCheckMs ?? 1e3, o.waitPingMs ?? 25e3);
|
|
550
|
+
res.writeHead(404, { "content-type": "text/plain; charset=utf-8" });
|
|
551
|
+
res.end("Rota reservada do Duto");
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
498
554
|
const id = nextId++;
|
|
499
555
|
const started = Date.now();
|
|
500
556
|
const headers = rewrite(req.headers, route);
|
|
@@ -957,9 +1013,9 @@ async function runSession(specs, source, flags) {
|
|
|
957
1013
|
const session = new Session({ api, deviceId: config.deviceId, deviceName: hostname(), cloudflaredPath, source });
|
|
958
1014
|
if (wantsUi(flags)) {
|
|
959
1015
|
const { runFullscreen } = await import("./fullscreen-5437X7MP.js");
|
|
960
|
-
process.exit(await runFullscreen(session, specs, "0.4.
|
|
1016
|
+
process.exit(await runFullscreen(session, specs, "0.4.5"));
|
|
961
1017
|
}
|
|
962
|
-
console.log(`${mark()} ${paint("muted", `${"0.4.
|
|
1018
|
+
console.log(`${mark()} ${paint("muted", `${"0.4.5"} \xB7 ${source} \xB7 Ctrl+C para encerrar`)}`);
|
|
963
1019
|
const stopLog = runPlain(session, (l) => console.log(l));
|
|
964
1020
|
const stopped = new Promise((resolve) => {
|
|
965
1021
|
const onSignal = () => {
|
|
@@ -1046,7 +1102,7 @@ cli.help(
|
|
|
1046
1102
|
body: s.body.replace(/^\s*For more info, run any command with the `--help` flag:/m, " Mais detalhes de um comando: duto <comando> --help").replace("Display this message", "Mostra esta ajuda").replace("Display version number", "Mostra a vers\xE3o")
|
|
1047
1103
|
}))
|
|
1048
1104
|
);
|
|
1049
|
-
cli.version("0.4.
|
|
1105
|
+
cli.version("0.4.5");
|
|
1050
1106
|
try {
|
|
1051
1107
|
cli.parse(process.argv, { run: false });
|
|
1052
1108
|
await cli.runMatchedCommand();
|