pty-manager 1.2.14 → 1.2.16
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/dist/index.d.mts +34 -1
- package/dist/index.d.ts +34 -1
- package/dist/index.js +75 -12
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +75 -12
- package/dist/index.mjs.map +1 -1
- package/dist/pty-worker.js +77 -12
- package/package.json +13 -14
package/dist/pty-worker.js
CHANGED
|
@@ -399,11 +399,11 @@ var PTYSession = class extends import_events.EventEmitter {
|
|
|
399
399
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
400
400
|
/**
|
|
401
401
|
* Start or reset the stall detection timer.
|
|
402
|
-
*
|
|
402
|
+
* Active when status is "busy" or "authenticating" and stall detection is enabled.
|
|
403
403
|
*/
|
|
404
404
|
resetStallTimer() {
|
|
405
405
|
this.clearStallTimer();
|
|
406
|
-
if (!this._stallDetectionEnabled || this._status !== "busy") {
|
|
406
|
+
if (!this._stallDetectionEnabled || this._status !== "busy" && this._status !== "authenticating") {
|
|
407
407
|
return;
|
|
408
408
|
}
|
|
409
409
|
this._stallStartedAt = Date.now();
|
|
@@ -426,7 +426,7 @@ var PTYSession = class extends import_events.EventEmitter {
|
|
|
426
426
|
* Called when the stall timer fires (no output for stallTimeoutMs).
|
|
427
427
|
*/
|
|
428
428
|
onStallTimerFired() {
|
|
429
|
-
if (this._status !== "busy") {
|
|
429
|
+
if (this._status !== "busy" && this._status !== "authenticating") {
|
|
430
430
|
return;
|
|
431
431
|
}
|
|
432
432
|
const tail = this.outputBuffer.slice(-500);
|
|
@@ -446,6 +446,12 @@ var PTYSession = class extends import_events.EventEmitter {
|
|
|
446
446
|
this.emit("stall_detected", recentOutput, stallDurationMs);
|
|
447
447
|
this._stallTimer = setTimeout(() => this.onStallTimerFired(), this._stallTimeoutMs);
|
|
448
448
|
}
|
|
449
|
+
/**
|
|
450
|
+
* Promise-based delay helper.
|
|
451
|
+
*/
|
|
452
|
+
delay(ms) {
|
|
453
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
454
|
+
}
|
|
449
455
|
/**
|
|
450
456
|
* Simple string hash for deduplication.
|
|
451
457
|
*/
|
|
@@ -471,7 +477,7 @@ var PTYSession = class extends import_events.EventEmitter {
|
|
|
471
477
|
* Called by the manager after onStallClassify resolves.
|
|
472
478
|
*/
|
|
473
479
|
handleStallClassification(classification) {
|
|
474
|
-
if (this._status !== "busy") {
|
|
480
|
+
if (this._status !== "busy" && this._status !== "authenticating") {
|
|
475
481
|
return;
|
|
476
482
|
}
|
|
477
483
|
if (!classification || classification.state === "still_working") {
|
|
@@ -490,7 +496,13 @@ var PTYSession = class extends import_events.EventEmitter {
|
|
|
490
496
|
{ sessionId: this.id, response: classification.suggestedResponse },
|
|
491
497
|
"Auto-responding to stall-classified prompt"
|
|
492
498
|
);
|
|
493
|
-
|
|
499
|
+
const resp = classification.suggestedResponse;
|
|
500
|
+
if (resp.startsWith("keys:")) {
|
|
501
|
+
const keys = resp.slice(5).split(",").map((k) => k.trim());
|
|
502
|
+
this.sendKeySequence(keys);
|
|
503
|
+
} else {
|
|
504
|
+
this.writeRaw(resp + "\r");
|
|
505
|
+
}
|
|
494
506
|
this.emit("blocking_prompt", promptInfo, true);
|
|
495
507
|
} else {
|
|
496
508
|
this.emit("blocking_prompt", promptInfo, false);
|
|
@@ -569,10 +581,14 @@ var PTYSession = class extends import_events.EventEmitter {
|
|
|
569
581
|
this.ptyProcess.onData((data) => {
|
|
570
582
|
this._lastActivityAt = /* @__PURE__ */ new Date();
|
|
571
583
|
this.outputBuffer += data;
|
|
572
|
-
if (this._status === "busy") {
|
|
584
|
+
if (this._status === "busy" || this._status === "authenticating") {
|
|
573
585
|
this.resetStallTimer();
|
|
574
586
|
}
|
|
575
587
|
this.emit("output", data);
|
|
588
|
+
const blockingPrompt = this.detectAndHandleBlockingPrompt();
|
|
589
|
+
if (blockingPrompt) {
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
576
592
|
if ((this._status === "starting" || this._status === "authenticating") && this.adapter.detectReady(this.outputBuffer)) {
|
|
577
593
|
this._status = "ready";
|
|
578
594
|
this._lastBlockingPromptHash = null;
|
|
@@ -582,10 +598,6 @@ var PTYSession = class extends import_events.EventEmitter {
|
|
|
582
598
|
this.logger.info({ sessionId: this.id }, "Session ready");
|
|
583
599
|
return;
|
|
584
600
|
}
|
|
585
|
-
const blockingPrompt = this.detectAndHandleBlockingPrompt();
|
|
586
|
-
if (blockingPrompt) {
|
|
587
|
-
return;
|
|
588
|
-
}
|
|
589
601
|
if (this._status !== "ready" && this._status !== "busy") {
|
|
590
602
|
const loginDetection = this.adapter.detectLogin(this.outputBuffer);
|
|
591
603
|
if (loginDetection.required && this._status !== "authenticating") {
|
|
@@ -653,7 +665,13 @@ var PTYSession = class extends import_events.EventEmitter {
|
|
|
653
665
|
},
|
|
654
666
|
"Auto-responding to blocking prompt"
|
|
655
667
|
);
|
|
656
|
-
|
|
668
|
+
const resp = detection.suggestedResponse;
|
|
669
|
+
if (resp.startsWith("keys:")) {
|
|
670
|
+
const keys = resp.slice(5).split(",").map((k) => k.trim());
|
|
671
|
+
this.sendKeySequence(keys);
|
|
672
|
+
} else {
|
|
673
|
+
this.writeRaw(resp + "\r");
|
|
674
|
+
}
|
|
657
675
|
this._lastBlockingPromptHash = null;
|
|
658
676
|
this.emit("blocking_prompt", promptInfo, true);
|
|
659
677
|
return true;
|
|
@@ -705,7 +723,15 @@ var PTYSession = class extends import_events.EventEmitter {
|
|
|
705
723
|
},
|
|
706
724
|
"Applying auto-response rule"
|
|
707
725
|
);
|
|
708
|
-
|
|
726
|
+
const useKeys = rule.keys && rule.keys.length > 0;
|
|
727
|
+
const isTuiDefault = !rule.responseType && !rule.keys && this.adapter.usesTuiMenus;
|
|
728
|
+
if (useKeys) {
|
|
729
|
+
this.sendKeySequence(rule.keys);
|
|
730
|
+
} else if (isTuiDefault) {
|
|
731
|
+
this.sendKeys("enter");
|
|
732
|
+
} else {
|
|
733
|
+
this.writeRaw(rule.response + "\r");
|
|
734
|
+
}
|
|
709
735
|
this.outputBuffer = this.outputBuffer.replace(rule.pattern, "");
|
|
710
736
|
const promptInfo = {
|
|
711
737
|
type: rule.type,
|
|
@@ -835,6 +861,26 @@ var PTYSession = class extends import_events.EventEmitter {
|
|
|
835
861
|
}
|
|
836
862
|
}
|
|
837
863
|
}
|
|
864
|
+
/**
|
|
865
|
+
* Select a TUI menu option by index (0-based).
|
|
866
|
+
* Sends Down arrow `optionIndex` times, then Enter, with 50ms delays.
|
|
867
|
+
*/
|
|
868
|
+
async selectMenuOption(optionIndex) {
|
|
869
|
+
for (let i = 0; i < optionIndex; i++) {
|
|
870
|
+
this.sendKeys("down");
|
|
871
|
+
await this.delay(50);
|
|
872
|
+
}
|
|
873
|
+
this.sendKeys("enter");
|
|
874
|
+
}
|
|
875
|
+
/**
|
|
876
|
+
* Send a sequence of keys with staggered timing.
|
|
877
|
+
* Each key is sent 50ms apart using setTimeout to keep the caller synchronous.
|
|
878
|
+
*/
|
|
879
|
+
sendKeySequence(keys) {
|
|
880
|
+
keys.forEach((key, i) => {
|
|
881
|
+
setTimeout(() => this.sendKeys(key), i * 50);
|
|
882
|
+
});
|
|
883
|
+
}
|
|
838
884
|
/**
|
|
839
885
|
* Paste text using bracketed paste mode
|
|
840
886
|
*
|
|
@@ -1552,6 +1598,8 @@ function deserializeRule(serialized) {
|
|
|
1552
1598
|
pattern: new RegExp(serialized.pattern, serialized.flags || ""),
|
|
1553
1599
|
type: serialized.type,
|
|
1554
1600
|
response: serialized.response,
|
|
1601
|
+
responseType: serialized.responseType,
|
|
1602
|
+
keys: serialized.keys,
|
|
1555
1603
|
description: serialized.description,
|
|
1556
1604
|
safe: serialized.safe
|
|
1557
1605
|
};
|
|
@@ -1562,6 +1610,8 @@ function serializeRule(rule) {
|
|
|
1562
1610
|
flags: rule.pattern.flags || void 0,
|
|
1563
1611
|
type: rule.type,
|
|
1564
1612
|
response: rule.response,
|
|
1613
|
+
responseType: rule.responseType,
|
|
1614
|
+
keys: rule.keys,
|
|
1565
1615
|
description: rule.description,
|
|
1566
1616
|
safe: rule.safe
|
|
1567
1617
|
};
|
|
@@ -1618,6 +1668,14 @@ function handleConfigureStallDetection(enabled, timeoutMs) {
|
|
|
1618
1668
|
ack("configureStallDetection", void 0, false, err instanceof Error ? err.message : String(err));
|
|
1619
1669
|
}
|
|
1620
1670
|
}
|
|
1671
|
+
function handleSelectMenuOption(id, optionIndex) {
|
|
1672
|
+
const session = manager.getSession(id);
|
|
1673
|
+
if (!session) {
|
|
1674
|
+
ack("selectMenuOption", id, false, `Session ${id} not found`);
|
|
1675
|
+
return;
|
|
1676
|
+
}
|
|
1677
|
+
session.selectMenuOption(optionIndex).then(() => ack("selectMenuOption", id, true)).catch((err) => ack("selectMenuOption", id, false, err instanceof Error ? err.message : String(err)));
|
|
1678
|
+
}
|
|
1621
1679
|
function handleClassifyStallResult(id, classification) {
|
|
1622
1680
|
try {
|
|
1623
1681
|
const session = manager.getSession(id);
|
|
@@ -1730,6 +1788,13 @@ function processCommand(line) {
|
|
|
1730
1788
|
}
|
|
1731
1789
|
handleClearRules(command.id);
|
|
1732
1790
|
break;
|
|
1791
|
+
case "selectMenuOption":
|
|
1792
|
+
if (!command.id || command.optionIndex === void 0) {
|
|
1793
|
+
ack("selectMenuOption", command.id, false, "Missing id or optionIndex");
|
|
1794
|
+
return;
|
|
1795
|
+
}
|
|
1796
|
+
handleSelectMenuOption(command.id, command.optionIndex);
|
|
1797
|
+
break;
|
|
1733
1798
|
case "configureStallDetection":
|
|
1734
1799
|
handleConfigureStallDetection(command.enabled ?? false, command.timeoutMs);
|
|
1735
1800
|
break;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pty-manager",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.16",
|
|
4
4
|
"description": "PTY session manager with lifecycle management, pluggable adapters, and blocking prompt detection",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -24,18 +24,6 @@
|
|
|
24
24
|
"README.md",
|
|
25
25
|
"LICENSE"
|
|
26
26
|
],
|
|
27
|
-
"scripts": {
|
|
28
|
-
"postinstall": "node scripts/postinstall.js",
|
|
29
|
-
"build": "tsup",
|
|
30
|
-
"dev": "tsup --watch",
|
|
31
|
-
"test": "vitest",
|
|
32
|
-
"test:run": "vitest run",
|
|
33
|
-
"test:coverage": "vitest --coverage",
|
|
34
|
-
"test:integration": "tsx test-integration.ts",
|
|
35
|
-
"type-check": "tsc --noEmit",
|
|
36
|
-
"clean": "rm -rf dist",
|
|
37
|
-
"prepublishOnly": "pnpm run build"
|
|
38
|
-
},
|
|
39
27
|
"keywords": [
|
|
40
28
|
"pty",
|
|
41
29
|
"terminal",
|
|
@@ -73,5 +61,16 @@
|
|
|
73
61
|
},
|
|
74
62
|
"engines": {
|
|
75
63
|
"node": ">=18"
|
|
64
|
+
},
|
|
65
|
+
"scripts": {
|
|
66
|
+
"postinstall": "node scripts/postinstall.js",
|
|
67
|
+
"build": "tsup",
|
|
68
|
+
"dev": "tsup --watch",
|
|
69
|
+
"test": "vitest",
|
|
70
|
+
"test:run": "vitest run",
|
|
71
|
+
"test:coverage": "vitest --coverage",
|
|
72
|
+
"test:integration": "tsx test-integration.ts",
|
|
73
|
+
"type-check": "tsc --noEmit",
|
|
74
|
+
"clean": "rm -rf dist"
|
|
76
75
|
}
|
|
77
|
-
}
|
|
76
|
+
}
|