pty-manager 1.2.20 → 1.2.22
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 +24 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +93 -8
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +93 -8
- package/dist/index.mjs.map +1 -1
- package/dist/pty-worker.js +88 -8
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -268,7 +268,7 @@ var SPECIAL_KEYS = {
|
|
|
268
268
|
};
|
|
269
269
|
var BRACKETED_PASTE_START = "\x1B[200~";
|
|
270
270
|
var BRACKETED_PASTE_END = "\x1B[201~";
|
|
271
|
-
var PTYSession = class extends EventEmitter {
|
|
271
|
+
var PTYSession = class _PTYSession extends EventEmitter {
|
|
272
272
|
constructor(adapter, config, logger, stallDetectionEnabled, defaultStallTimeoutMs) {
|
|
273
273
|
super();
|
|
274
274
|
this.adapter = adapter;
|
|
@@ -277,6 +277,15 @@ var PTYSession = class extends EventEmitter {
|
|
|
277
277
|
this.logger = logger || consoleLogger;
|
|
278
278
|
this._stallDetectionEnabled = stallDetectionEnabled ?? false;
|
|
279
279
|
this._stallTimeoutMs = config.stallTimeoutMs ?? defaultStallTimeoutMs ?? 8e3;
|
|
280
|
+
if (config.ruleOverrides) {
|
|
281
|
+
for (const [key, value] of Object.entries(config.ruleOverrides)) {
|
|
282
|
+
if (value === null) {
|
|
283
|
+
this._disabledRulePatterns.add(key);
|
|
284
|
+
} else {
|
|
285
|
+
this._ruleOverrides.set(key, value);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
280
289
|
}
|
|
281
290
|
ptyProcess = null;
|
|
282
291
|
outputBuffer = "";
|
|
@@ -288,6 +297,8 @@ var PTYSession = class extends EventEmitter {
|
|
|
288
297
|
sessionRules = [];
|
|
289
298
|
_firedOnceRules = /* @__PURE__ */ new Set();
|
|
290
299
|
_lastBlockingPromptHash = null;
|
|
300
|
+
_ruleOverrides = /* @__PURE__ */ new Map();
|
|
301
|
+
_disabledRulePatterns = /* @__PURE__ */ new Set();
|
|
291
302
|
// Stall detection
|
|
292
303
|
_stallTimer = null;
|
|
293
304
|
_stallTimeoutMs;
|
|
@@ -295,6 +306,9 @@ var PTYSession = class extends EventEmitter {
|
|
|
295
306
|
_lastStallHash = null;
|
|
296
307
|
_stallStartedAt = null;
|
|
297
308
|
_lastContentHash = null;
|
|
309
|
+
// Task completion detection (idle detection when busy)
|
|
310
|
+
_taskCompleteTimer = null;
|
|
311
|
+
static TASK_COMPLETE_DEBOUNCE_MS = 1500;
|
|
298
312
|
id;
|
|
299
313
|
config;
|
|
300
314
|
get status() {
|
|
@@ -470,10 +484,12 @@ var PTYSession = class extends EventEmitter {
|
|
|
470
484
|
* word boundaries — e.g. "Do\x1b[5Cyou" becomes "Do you", not "Doyou".
|
|
471
485
|
*/
|
|
472
486
|
stripAnsiForStall(str) {
|
|
473
|
-
let result = str.replace(/\x1b\[\d*[
|
|
487
|
+
let result = str.replace(/\x1b\[\d*[CDABGdEF]/g, " ");
|
|
474
488
|
result = result.replace(/\x1b\[\d*(?:;\d+)?[Hf]/g, " ");
|
|
489
|
+
result = result.replace(/\x1b\[\d*[JK]/g, " ");
|
|
475
490
|
result = result.replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, "");
|
|
476
|
-
result = result.replace(/[
|
|
491
|
+
result = result.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, "");
|
|
492
|
+
result = result.replace(/[│╭╰╮╯─═╌║╔╗╚╝╠╣╦╩╬┌┐└┘├┤┬┴┼●○❯❮▶◀⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏⣾⣽⣻⢿⡿⣟⣯⣷✻✶✳✢⏺←→↑↓⬆⬇◆◇▪▫■□▲△▼▽◈⟨⟩⌘⏎⏏⌫⌦⇧⇪⌥]/g, " ");
|
|
477
493
|
result = result.replace(/ {2,}/g, " ");
|
|
478
494
|
return result;
|
|
479
495
|
}
|
|
@@ -510,6 +526,7 @@ var PTYSession = class extends EventEmitter {
|
|
|
510
526
|
this.writeRaw(resp + "\r");
|
|
511
527
|
}
|
|
512
528
|
this.emit("blocking_prompt", promptInfo, true);
|
|
529
|
+
this.outputBuffer = "";
|
|
513
530
|
} else {
|
|
514
531
|
this.emit("blocking_prompt", promptInfo, false);
|
|
515
532
|
}
|
|
@@ -530,6 +547,39 @@ var PTYSession = class extends EventEmitter {
|
|
|
530
547
|
}
|
|
531
548
|
}
|
|
532
549
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
550
|
+
// Task Completion Detection
|
|
551
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
552
|
+
/**
|
|
553
|
+
* Schedule a task_complete transition after a debounce period.
|
|
554
|
+
* If new non-whitespace output arrives before the timer fires,
|
|
555
|
+
* the timer is cancelled (by cancelTaskComplete in onData).
|
|
556
|
+
*/
|
|
557
|
+
scheduleTaskComplete() {
|
|
558
|
+
if (this._taskCompleteTimer) return;
|
|
559
|
+
this._taskCompleteTimer = setTimeout(() => {
|
|
560
|
+
this._taskCompleteTimer = null;
|
|
561
|
+
if (this._status !== "busy") return;
|
|
562
|
+
if (!this.adapter.detectReady(this.outputBuffer)) return;
|
|
563
|
+
this._status = "ready";
|
|
564
|
+
this._lastBlockingPromptHash = null;
|
|
565
|
+
this.outputBuffer = "";
|
|
566
|
+
this.clearStallTimer();
|
|
567
|
+
this.emit("status_changed", "ready");
|
|
568
|
+
this.emit("task_complete");
|
|
569
|
+
this.logger.info({ sessionId: this.id }, "Task complete \u2014 agent returned to idle prompt");
|
|
570
|
+
}, _PTYSession.TASK_COMPLETE_DEBOUNCE_MS);
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* Cancel a pending task_complete timer (new output arrived that
|
|
574
|
+
* doesn't match the idle prompt, so the agent is still working).
|
|
575
|
+
*/
|
|
576
|
+
cancelTaskComplete() {
|
|
577
|
+
if (this._taskCompleteTimer) {
|
|
578
|
+
clearTimeout(this._taskCompleteTimer);
|
|
579
|
+
this._taskCompleteTimer = null;
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
533
583
|
// Lifecycle
|
|
534
584
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
535
585
|
/**
|
|
@@ -591,10 +641,6 @@ var PTYSession = class extends EventEmitter {
|
|
|
591
641
|
this.resetStallTimer();
|
|
592
642
|
}
|
|
593
643
|
this.emit("output", data);
|
|
594
|
-
const blockingPrompt = this.detectAndHandleBlockingPrompt();
|
|
595
|
-
if (blockingPrompt) {
|
|
596
|
-
return;
|
|
597
|
-
}
|
|
598
644
|
if ((this._status === "starting" || this._status === "authenticating") && this.adapter.detectReady(this.outputBuffer)) {
|
|
599
645
|
this._status = "ready";
|
|
600
646
|
this._lastBlockingPromptHash = null;
|
|
@@ -604,6 +650,15 @@ var PTYSession = class extends EventEmitter {
|
|
|
604
650
|
this.logger.info({ sessionId: this.id }, "Session ready");
|
|
605
651
|
return;
|
|
606
652
|
}
|
|
653
|
+
if (this._status === "busy" && this.adapter.detectReady(this.outputBuffer)) {
|
|
654
|
+
this.scheduleTaskComplete();
|
|
655
|
+
} else {
|
|
656
|
+
this.cancelTaskComplete();
|
|
657
|
+
}
|
|
658
|
+
const blockingPrompt = this.detectAndHandleBlockingPrompt();
|
|
659
|
+
if (blockingPrompt) {
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
607
662
|
if (this._status !== "ready" && this._status !== "busy") {
|
|
608
663
|
const loginDetection = this.adapter.detectLogin(this.outputBuffer);
|
|
609
664
|
if (loginDetection.required && this._status !== "authenticating") {
|
|
@@ -679,6 +734,7 @@ var PTYSession = class extends EventEmitter {
|
|
|
679
734
|
this.writeRaw(resp + "\r");
|
|
680
735
|
}
|
|
681
736
|
this._lastBlockingPromptHash = null;
|
|
737
|
+
this.outputBuffer = "";
|
|
682
738
|
this.emit("blocking_prompt", promptInfo, true);
|
|
683
739
|
return true;
|
|
684
740
|
}
|
|
@@ -706,7 +762,10 @@ var PTYSession = class extends EventEmitter {
|
|
|
706
762
|
* Session rules are checked first, then adapter rules.
|
|
707
763
|
*/
|
|
708
764
|
tryAutoResponse() {
|
|
709
|
-
const adapterRules = this.adapter.autoResponseRules || []
|
|
765
|
+
const adapterRules = (this.adapter.autoResponseRules || []).filter((r) => !this._disabledRulePatterns.has(r.pattern.source)).map((r) => {
|
|
766
|
+
const override = this._ruleOverrides.get(r.pattern.source);
|
|
767
|
+
return override ? { ...r, ...override } : r;
|
|
768
|
+
});
|
|
710
769
|
const allRules = [...this.sessionRules, ...adapterRules];
|
|
711
770
|
if (allRules.length === 0) {
|
|
712
771
|
return false;
|
|
@@ -822,6 +881,7 @@ var PTYSession = class extends EventEmitter {
|
|
|
822
881
|
*/
|
|
823
882
|
send(message) {
|
|
824
883
|
this._status = "busy";
|
|
884
|
+
this.emit("status_changed", "busy");
|
|
825
885
|
this.resetStallTimer();
|
|
826
886
|
const msg = {
|
|
827
887
|
id: `${this.id}-msg-${++this.messageCounter}`,
|
|
@@ -932,6 +992,7 @@ var PTYSession = class extends EventEmitter {
|
|
|
932
992
|
if (this.ptyProcess) {
|
|
933
993
|
this._status = "stopping";
|
|
934
994
|
this.clearStallTimer();
|
|
995
|
+
this.cancelTaskComplete();
|
|
935
996
|
this.ptyProcess.kill(signal);
|
|
936
997
|
this.logger.info({ sessionId: this.id, signal }, "Killing PTY session");
|
|
937
998
|
}
|
|
@@ -1085,6 +1146,12 @@ var PTYManager = class extends EventEmitter2 {
|
|
|
1085
1146
|
session.on("error", (error) => {
|
|
1086
1147
|
this.emit("session_error", session.toHandle(), error.message);
|
|
1087
1148
|
});
|
|
1149
|
+
session.on("status_changed", () => {
|
|
1150
|
+
this.emit("session_status_changed", session.toHandle());
|
|
1151
|
+
});
|
|
1152
|
+
session.on("task_complete", () => {
|
|
1153
|
+
this.emit("task_complete", session.toHandle());
|
|
1154
|
+
});
|
|
1088
1155
|
session.on("stall_detected", (recentOutput, stallDurationMs) => {
|
|
1089
1156
|
const handle = session.toHandle();
|
|
1090
1157
|
this.emit("stall_detected", handle, recentOutput, stallDurationMs);
|
|
@@ -1938,6 +2005,24 @@ var BunCompatiblePTYManager = class extends EventEmitter3 {
|
|
|
1938
2005
|
}
|
|
1939
2006
|
break;
|
|
1940
2007
|
}
|
|
2008
|
+
case "status_changed": {
|
|
2009
|
+
const session = this.sessions.get(id);
|
|
2010
|
+
if (session) {
|
|
2011
|
+
session.status = event.status;
|
|
2012
|
+
session.lastActivityAt = /* @__PURE__ */ new Date();
|
|
2013
|
+
this.emit("session_status_changed", session);
|
|
2014
|
+
}
|
|
2015
|
+
break;
|
|
2016
|
+
}
|
|
2017
|
+
case "task_complete": {
|
|
2018
|
+
const session = this.sessions.get(id);
|
|
2019
|
+
if (session) {
|
|
2020
|
+
session.status = "ready";
|
|
2021
|
+
session.lastActivityAt = /* @__PURE__ */ new Date();
|
|
2022
|
+
this.emit("task_complete", session);
|
|
2023
|
+
}
|
|
2024
|
+
break;
|
|
2025
|
+
}
|
|
1941
2026
|
case "stall_detected": {
|
|
1942
2027
|
const session = this.sessions.get(id);
|
|
1943
2028
|
if (session) {
|