muuuuse 2.2.0 → 2.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/package.json +1 -1
- package/src/runtime.js +86 -14
package/package.json
CHANGED
package/src/runtime.js
CHANGED
|
@@ -35,7 +35,9 @@ const {
|
|
|
35
35
|
writeJson,
|
|
36
36
|
} = require("./util");
|
|
37
37
|
|
|
38
|
-
const
|
|
38
|
+
const TYPE_CHUNK_DELAY_MS = 12;
|
|
39
|
+
const TYPE_CHUNK_SIZE = 6;
|
|
40
|
+
const ESCAPE_RELAY_GUARD_MS = 180;
|
|
39
41
|
const MIRROR_SUPPRESSION_WINDOW_MS = 30 * 1000;
|
|
40
42
|
const PENDING_RELAY_CONTEXT_TTL_MS = 2 * 60 * 1000;
|
|
41
43
|
const EMITTED_ANSWER_TTL_MS = 5 * 60 * 1000;
|
|
@@ -435,24 +437,77 @@ function readSeatChallenge(paths, sessionName) {
|
|
|
435
437
|
};
|
|
436
438
|
}
|
|
437
439
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
+
function normalizeRelayPayloadForTyping(text) {
|
|
441
|
+
return String(text || "")
|
|
440
442
|
.replace(/\r/g, "")
|
|
441
443
|
.replace(/\s*\n+\s*/g, " ")
|
|
442
444
|
.replace(/[ \t]{2,}/g, " ")
|
|
443
445
|
.trim();
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
function chunkRelayPayloadForTyping(text, chunkSize = TYPE_CHUNK_SIZE) {
|
|
449
|
+
const normalized = String(text || "");
|
|
450
|
+
if (!normalized) {
|
|
451
|
+
return [];
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
const size = Number.isInteger(chunkSize) && chunkSize > 0 ? chunkSize : TYPE_CHUNK_SIZE;
|
|
455
|
+
const chunks = [];
|
|
456
|
+
for (let index = 0; index < normalized.length; index += size) {
|
|
457
|
+
chunks.push(normalized.slice(index, index + size));
|
|
458
|
+
}
|
|
459
|
+
return chunks;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
function stripPassiveTerminalInput(input) {
|
|
463
|
+
return String(input || "")
|
|
464
|
+
.replace(/\u001b\][\s\S]*?(?:\u0007|\u001b\\)/g, "")
|
|
465
|
+
.replace(/\u001b\[(?:I|O)/g, "")
|
|
466
|
+
.replace(/\u001b\[\d+;\d+R/g, "")
|
|
467
|
+
.replace(/\u001b\[\?[0-9;]*c/g, "")
|
|
468
|
+
.replace(/\u0000/g, "");
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
function isBareEscapeInput(input) {
|
|
472
|
+
return String(input || "") === "\u001b";
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
function isMeaningfulTerminalInput(input) {
|
|
476
|
+
if (isBareEscapeInput(input)) {
|
|
477
|
+
return false;
|
|
478
|
+
}
|
|
479
|
+
return stripPassiveTerminalInput(input).length > 0;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
function getEscapeRelayDelayMs(lastEscapeAtMs, now = Date.now()) {
|
|
483
|
+
if (!Number.isFinite(lastEscapeAtMs) || lastEscapeAtMs <= 0) {
|
|
484
|
+
return 0;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
const elapsedMs = now - lastEscapeAtMs;
|
|
488
|
+
if (elapsedMs >= ESCAPE_RELAY_GUARD_MS) {
|
|
489
|
+
return 0;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
return ESCAPE_RELAY_GUARD_MS - elapsedMs;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
async function sendTextAndEnter(child, text, shouldAbort = () => false) {
|
|
496
|
+
const payload = normalizeRelayPayloadForTyping(text);
|
|
444
497
|
|
|
445
498
|
if (payload.length > 0) {
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
499
|
+
for (const chunk of chunkRelayPayloadForTyping(payload)) {
|
|
500
|
+
if (shouldAbort() || !child) {
|
|
501
|
+
return false;
|
|
502
|
+
}
|
|
449
503
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
504
|
+
try {
|
|
505
|
+
child.write(chunk);
|
|
506
|
+
} catch {
|
|
507
|
+
return false;
|
|
508
|
+
}
|
|
509
|
+
await sleep(TYPE_CHUNK_DELAY_MS);
|
|
454
510
|
}
|
|
455
|
-
await sleep(TYPE_DELAY_MS);
|
|
456
511
|
}
|
|
457
512
|
|
|
458
513
|
if (shouldAbort() || !child) {
|
|
@@ -496,6 +551,7 @@ class ArmedSeat {
|
|
|
496
551
|
this.forceKillTimer = null;
|
|
497
552
|
this.identity = null;
|
|
498
553
|
this.lastUserInputAtMs = 0;
|
|
554
|
+
this.lastEscapeAtMs = 0;
|
|
499
555
|
this.pendingInboundContext = null;
|
|
500
556
|
this.recentInboundRelays = [];
|
|
501
557
|
this.recentEmittedAnswers = [];
|
|
@@ -772,12 +828,18 @@ class ArmedSeat {
|
|
|
772
828
|
|
|
773
829
|
installStdinProxy() {
|
|
774
830
|
const handleData = (chunk) => {
|
|
775
|
-
|
|
776
|
-
|
|
831
|
+
const chunkText = chunk.toString("utf8");
|
|
832
|
+
if (isBareEscapeInput(chunkText)) {
|
|
833
|
+
this.lastEscapeAtMs = Date.now();
|
|
834
|
+
}
|
|
835
|
+
if (isMeaningfulTerminalInput(chunkText)) {
|
|
836
|
+
this.lastUserInputAtMs = Date.now();
|
|
837
|
+
this.pendingInboundContext = null;
|
|
838
|
+
}
|
|
777
839
|
if (!this.child) {
|
|
778
840
|
return;
|
|
779
841
|
}
|
|
780
|
-
this.child.write(
|
|
842
|
+
this.child.write(chunkText);
|
|
781
843
|
};
|
|
782
844
|
|
|
783
845
|
const handleEnd = () => {
|
|
@@ -932,6 +994,11 @@ class ArmedSeat {
|
|
|
932
994
|
continue;
|
|
933
995
|
}
|
|
934
996
|
|
|
997
|
+
const escapeRelayDelayMs = getEscapeRelayDelayMs(this.lastEscapeAtMs);
|
|
998
|
+
if (escapeRelayDelayMs > 0) {
|
|
999
|
+
await sleep(escapeRelayDelayMs);
|
|
1000
|
+
}
|
|
1001
|
+
|
|
935
1002
|
const delivered = await sendTextAndEnter(
|
|
936
1003
|
this.child,
|
|
937
1004
|
payload,
|
|
@@ -1475,7 +1542,12 @@ function stopAllSessions() {
|
|
|
1475
1542
|
module.exports = {
|
|
1476
1543
|
ArmedSeat,
|
|
1477
1544
|
buildChildEnv,
|
|
1545
|
+
chunkRelayPayloadForTyping,
|
|
1546
|
+
getEscapeRelayDelayMs,
|
|
1478
1547
|
getStatusReport,
|
|
1548
|
+
isBareEscapeInput,
|
|
1549
|
+
isMeaningfulTerminalInput,
|
|
1550
|
+
normalizeRelayPayloadForTyping,
|
|
1479
1551
|
resolveSessionName,
|
|
1480
1552
|
stopAllSessions,
|
|
1481
1553
|
};
|