pty-manager 1.2.13 → 1.2.15
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 +114 -2
- package/dist/index.d.ts +114 -2
- package/dist/index.js +288 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +288 -6
- package/dist/index.mjs.map +1 -1
- package/dist/pty-worker.js +290 -5
- package/package.json +13 -14
package/dist/pty-worker.js
CHANGED
|
@@ -290,12 +290,14 @@ var SPECIAL_KEYS = {
|
|
|
290
290
|
var BRACKETED_PASTE_START = "\x1B[200~";
|
|
291
291
|
var BRACKETED_PASTE_END = "\x1B[201~";
|
|
292
292
|
var PTYSession = class extends import_events.EventEmitter {
|
|
293
|
-
constructor(adapter, config, logger) {
|
|
293
|
+
constructor(adapter, config, logger, stallDetectionEnabled, defaultStallTimeoutMs) {
|
|
294
294
|
super();
|
|
295
295
|
this.adapter = adapter;
|
|
296
296
|
this.id = config.id || generateId();
|
|
297
297
|
this.config = { ...config, id: this.id };
|
|
298
298
|
this.logger = logger || consoleLogger;
|
|
299
|
+
this._stallDetectionEnabled = stallDetectionEnabled ?? false;
|
|
300
|
+
this._stallTimeoutMs = config.stallTimeoutMs ?? defaultStallTimeoutMs ?? 8e3;
|
|
299
301
|
}
|
|
300
302
|
ptyProcess = null;
|
|
301
303
|
outputBuffer = "";
|
|
@@ -306,6 +308,12 @@ var PTYSession = class extends import_events.EventEmitter {
|
|
|
306
308
|
logger;
|
|
307
309
|
sessionRules = [];
|
|
308
310
|
_lastBlockingPromptHash = null;
|
|
311
|
+
// Stall detection
|
|
312
|
+
_stallTimer = null;
|
|
313
|
+
_stallTimeoutMs;
|
|
314
|
+
_stallDetectionEnabled;
|
|
315
|
+
_lastStallHash = null;
|
|
316
|
+
_stallStartedAt = null;
|
|
309
317
|
id;
|
|
310
318
|
config;
|
|
311
319
|
get status() {
|
|
@@ -387,6 +395,135 @@ var PTYSession = class extends import_events.EventEmitter {
|
|
|
387
395
|
this.logger.debug({ sessionId: this.id }, "Cleared auto-response rules");
|
|
388
396
|
}
|
|
389
397
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
398
|
+
// Stall Detection
|
|
399
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
400
|
+
/**
|
|
401
|
+
* Start or reset the stall detection timer.
|
|
402
|
+
* Only active when status is "busy" and stall detection is enabled.
|
|
403
|
+
*/
|
|
404
|
+
resetStallTimer() {
|
|
405
|
+
this.clearStallTimer();
|
|
406
|
+
if (!this._stallDetectionEnabled || this._status !== "busy") {
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
this._stallStartedAt = Date.now();
|
|
410
|
+
this._lastStallHash = null;
|
|
411
|
+
this._stallTimer = setTimeout(() => {
|
|
412
|
+
this.onStallTimerFired();
|
|
413
|
+
}, this._stallTimeoutMs);
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Clear the stall detection timer.
|
|
417
|
+
*/
|
|
418
|
+
clearStallTimer() {
|
|
419
|
+
if (this._stallTimer) {
|
|
420
|
+
clearTimeout(this._stallTimer);
|
|
421
|
+
this._stallTimer = null;
|
|
422
|
+
}
|
|
423
|
+
this._stallStartedAt = null;
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Called when the stall timer fires (no output for stallTimeoutMs).
|
|
427
|
+
*/
|
|
428
|
+
onStallTimerFired() {
|
|
429
|
+
if (this._status !== "busy") {
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
const tail = this.outputBuffer.slice(-500);
|
|
433
|
+
const hash = this.simpleHash(tail);
|
|
434
|
+
if (hash === this._lastStallHash) {
|
|
435
|
+
this._stallTimer = setTimeout(() => this.onStallTimerFired(), this._stallTimeoutMs);
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
this._lastStallHash = hash;
|
|
439
|
+
const recentRaw = this.outputBuffer.slice(-2e3);
|
|
440
|
+
const recentOutput = this.stripAnsiForStall(recentRaw);
|
|
441
|
+
const stallDurationMs = this._stallStartedAt ? Date.now() - this._stallStartedAt : this._stallTimeoutMs;
|
|
442
|
+
this.logger.debug(
|
|
443
|
+
{ sessionId: this.id, stallDurationMs, bufferTailLength: tail.length },
|
|
444
|
+
"Stall detected"
|
|
445
|
+
);
|
|
446
|
+
this.emit("stall_detected", recentOutput, stallDurationMs);
|
|
447
|
+
this._stallTimer = setTimeout(() => this.onStallTimerFired(), this._stallTimeoutMs);
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Promise-based delay helper.
|
|
451
|
+
*/
|
|
452
|
+
delay(ms) {
|
|
453
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* Simple string hash for deduplication.
|
|
457
|
+
*/
|
|
458
|
+
simpleHash(str) {
|
|
459
|
+
let hash = 0;
|
|
460
|
+
for (let i = 0; i < str.length; i++) {
|
|
461
|
+
const char = str.charCodeAt(i);
|
|
462
|
+
hash = (hash << 5) - hash + char;
|
|
463
|
+
hash |= 0;
|
|
464
|
+
}
|
|
465
|
+
return hash.toString(36);
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Strip ANSI codes for stall detection output.
|
|
469
|
+
* Replaces cursor-forward sequences with spaces first.
|
|
470
|
+
*/
|
|
471
|
+
stripAnsiForStall(str) {
|
|
472
|
+
const withSpaces = str.replace(/\x1b\[\d*C/g, " ");
|
|
473
|
+
return withSpaces.replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, "");
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* Handle external stall classification result.
|
|
477
|
+
* Called by the manager after onStallClassify resolves.
|
|
478
|
+
*/
|
|
479
|
+
handleStallClassification(classification) {
|
|
480
|
+
if (this._status !== "busy") {
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
if (!classification || classification.state === "still_working") {
|
|
484
|
+
this.resetStallTimer();
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
switch (classification.state) {
|
|
488
|
+
case "waiting_for_input": {
|
|
489
|
+
const promptInfo = {
|
|
490
|
+
type: "stall_classified",
|
|
491
|
+
prompt: classification.prompt,
|
|
492
|
+
canAutoRespond: !!classification.suggestedResponse
|
|
493
|
+
};
|
|
494
|
+
if (classification.suggestedResponse) {
|
|
495
|
+
this.logger.info(
|
|
496
|
+
{ sessionId: this.id, response: classification.suggestedResponse },
|
|
497
|
+
"Auto-responding to stall-classified prompt"
|
|
498
|
+
);
|
|
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
|
+
}
|
|
506
|
+
this.emit("blocking_prompt", promptInfo, true);
|
|
507
|
+
} else {
|
|
508
|
+
this.emit("blocking_prompt", promptInfo, false);
|
|
509
|
+
}
|
|
510
|
+
break;
|
|
511
|
+
}
|
|
512
|
+
case "task_complete":
|
|
513
|
+
this._status = "ready";
|
|
514
|
+
this._lastBlockingPromptHash = null;
|
|
515
|
+
this.outputBuffer = "";
|
|
516
|
+
this.clearStallTimer();
|
|
517
|
+
this.emit("ready");
|
|
518
|
+
this.logger.info({ sessionId: this.id }, "Stall classified as task_complete, transitioning to ready");
|
|
519
|
+
break;
|
|
520
|
+
case "error":
|
|
521
|
+
this.clearStallTimer();
|
|
522
|
+
this.emit("error", new Error(classification.prompt || "Stall classified as error"));
|
|
523
|
+
break;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
390
527
|
// Lifecycle
|
|
391
528
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
392
529
|
/**
|
|
@@ -444,11 +581,15 @@ var PTYSession = class extends import_events.EventEmitter {
|
|
|
444
581
|
this.ptyProcess.onData((data) => {
|
|
445
582
|
this._lastActivityAt = /* @__PURE__ */ new Date();
|
|
446
583
|
this.outputBuffer += data;
|
|
584
|
+
if (this._status === "busy") {
|
|
585
|
+
this.resetStallTimer();
|
|
586
|
+
}
|
|
447
587
|
this.emit("output", data);
|
|
448
588
|
if ((this._status === "starting" || this._status === "authenticating") && this.adapter.detectReady(this.outputBuffer)) {
|
|
449
589
|
this._status = "ready";
|
|
450
590
|
this._lastBlockingPromptHash = null;
|
|
451
591
|
this.outputBuffer = "";
|
|
592
|
+
this.clearStallTimer();
|
|
452
593
|
this.emit("ready");
|
|
453
594
|
this.logger.info({ sessionId: this.id }, "Session ready");
|
|
454
595
|
return;
|
|
@@ -461,6 +602,7 @@ var PTYSession = class extends import_events.EventEmitter {
|
|
|
461
602
|
const loginDetection = this.adapter.detectLogin(this.outputBuffer);
|
|
462
603
|
if (loginDetection.required && this._status !== "authenticating") {
|
|
463
604
|
this._status = "authenticating";
|
|
605
|
+
this.clearStallTimer();
|
|
464
606
|
this.emit("login_required", loginDetection.instructions, loginDetection.url);
|
|
465
607
|
this.logger.warn(
|
|
466
608
|
{ sessionId: this.id, loginType: loginDetection.type },
|
|
@@ -472,6 +614,7 @@ var PTYSession = class extends import_events.EventEmitter {
|
|
|
472
614
|
const exitDetection = this.adapter.detectExit(this.outputBuffer);
|
|
473
615
|
if (exitDetection.exited) {
|
|
474
616
|
this._status = "stopped";
|
|
617
|
+
this.clearStallTimer();
|
|
475
618
|
this.emit("exit", exitDetection.code || 0);
|
|
476
619
|
}
|
|
477
620
|
if (this._status !== "starting" && this._status !== "authenticating") {
|
|
@@ -480,6 +623,7 @@ var PTYSession = class extends import_events.EventEmitter {
|
|
|
480
623
|
});
|
|
481
624
|
this.ptyProcess.onExit(({ exitCode, signal }) => {
|
|
482
625
|
this._status = "stopped";
|
|
626
|
+
this.clearStallTimer();
|
|
483
627
|
this.logger.info(
|
|
484
628
|
{ sessionId: this.id, exitCode, signal },
|
|
485
629
|
"PTY session exited"
|
|
@@ -521,7 +665,13 @@ var PTYSession = class extends import_events.EventEmitter {
|
|
|
521
665
|
},
|
|
522
666
|
"Auto-responding to blocking prompt"
|
|
523
667
|
);
|
|
524
|
-
|
|
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
|
+
}
|
|
525
675
|
this._lastBlockingPromptHash = null;
|
|
526
676
|
this.emit("blocking_prompt", promptInfo, true);
|
|
527
677
|
return true;
|
|
@@ -555,8 +705,11 @@ var PTYSession = class extends import_events.EventEmitter {
|
|
|
555
705
|
if (allRules.length === 0) {
|
|
556
706
|
return false;
|
|
557
707
|
}
|
|
708
|
+
let stripped = this.stripAnsiForStall(this.outputBuffer);
|
|
709
|
+
stripped = stripped.replace(/[│╭╰╮╯─═╌║╔╗╚╝╠╣╦╩╬┌┐└┘├┤┬┴┼●○❯❮▶◀⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏⏺←→↑↓]/g, " ");
|
|
710
|
+
stripped = stripped.replace(/ {2,}/g, " ");
|
|
558
711
|
for (const rule of allRules) {
|
|
559
|
-
if (rule.pattern.test(
|
|
712
|
+
if (rule.pattern.test(stripped)) {
|
|
560
713
|
const safe = rule.safe !== false;
|
|
561
714
|
const isSessionRule = this.sessionRules.includes(rule);
|
|
562
715
|
if (safe) {
|
|
@@ -570,7 +723,15 @@ var PTYSession = class extends import_events.EventEmitter {
|
|
|
570
723
|
},
|
|
571
724
|
"Applying auto-response rule"
|
|
572
725
|
);
|
|
573
|
-
|
|
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
|
+
}
|
|
574
735
|
this.outputBuffer = this.outputBuffer.replace(rule.pattern, "");
|
|
575
736
|
const promptInfo = {
|
|
576
737
|
type: rule.type,
|
|
@@ -647,6 +808,7 @@ var PTYSession = class extends import_events.EventEmitter {
|
|
|
647
808
|
*/
|
|
648
809
|
send(message) {
|
|
649
810
|
this._status = "busy";
|
|
811
|
+
this.resetStallTimer();
|
|
650
812
|
const msg = {
|
|
651
813
|
id: `${this.id}-msg-${++this.messageCounter}`,
|
|
652
814
|
sessionId: this.id,
|
|
@@ -699,6 +861,26 @@ var PTYSession = class extends import_events.EventEmitter {
|
|
|
699
861
|
}
|
|
700
862
|
}
|
|
701
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
|
+
}
|
|
702
884
|
/**
|
|
703
885
|
* Paste text using bracketed paste mode
|
|
704
886
|
*
|
|
@@ -735,6 +917,7 @@ var PTYSession = class extends import_events.EventEmitter {
|
|
|
735
917
|
kill(signal) {
|
|
736
918
|
if (this.ptyProcess) {
|
|
737
919
|
this._status = "stopping";
|
|
920
|
+
this.clearStallTimer();
|
|
738
921
|
this.ptyProcess.kill(signal);
|
|
739
922
|
this.logger.info({ sessionId: this.id, signal }, "Killing PTY session");
|
|
740
923
|
}
|
|
@@ -804,11 +987,18 @@ var PTYManager = class extends import_events2.EventEmitter {
|
|
|
804
987
|
maxLogLines;
|
|
805
988
|
logger;
|
|
806
989
|
adapters;
|
|
990
|
+
// Stall detection config
|
|
991
|
+
_stallDetectionEnabled;
|
|
992
|
+
_stallTimeoutMs;
|
|
993
|
+
_onStallClassify;
|
|
807
994
|
constructor(config = {}) {
|
|
808
995
|
super();
|
|
809
996
|
this.adapters = new AdapterRegistry();
|
|
810
997
|
this.logger = config.logger || consoleLogger2;
|
|
811
998
|
this.maxLogLines = config.maxLogLines || 1e3;
|
|
999
|
+
this._stallDetectionEnabled = config.stallDetectionEnabled ?? false;
|
|
1000
|
+
this._stallTimeoutMs = config.stallTimeoutMs ?? 8e3;
|
|
1001
|
+
this._onStallClassify = config.onStallClassify;
|
|
812
1002
|
}
|
|
813
1003
|
/**
|
|
814
1004
|
* Register a CLI adapter
|
|
@@ -831,7 +1021,13 @@ var PTYManager = class extends import_events2.EventEmitter {
|
|
|
831
1021
|
{ type: config.type, name: config.name },
|
|
832
1022
|
"Spawning session"
|
|
833
1023
|
);
|
|
834
|
-
const session = new PTYSession(
|
|
1024
|
+
const session = new PTYSession(
|
|
1025
|
+
adapter,
|
|
1026
|
+
config,
|
|
1027
|
+
this.logger,
|
|
1028
|
+
this._stallDetectionEnabled,
|
|
1029
|
+
this._stallTimeoutMs
|
|
1030
|
+
);
|
|
835
1031
|
this.setupSessionEvents(session);
|
|
836
1032
|
this.sessions.set(session.id, session);
|
|
837
1033
|
this.outputLogs.set(session.id, []);
|
|
@@ -875,6 +1071,21 @@ var PTYManager = class extends import_events2.EventEmitter {
|
|
|
875
1071
|
session.on("error", (error) => {
|
|
876
1072
|
this.emit("session_error", session.toHandle(), error.message);
|
|
877
1073
|
});
|
|
1074
|
+
session.on("stall_detected", (recentOutput, stallDurationMs) => {
|
|
1075
|
+
const handle = session.toHandle();
|
|
1076
|
+
this.emit("stall_detected", handle, recentOutput, stallDurationMs);
|
|
1077
|
+
if (this._onStallClassify) {
|
|
1078
|
+
this._onStallClassify(session.id, recentOutput, stallDurationMs).then((classification) => {
|
|
1079
|
+
session.handleStallClassification(classification);
|
|
1080
|
+
}).catch((err) => {
|
|
1081
|
+
this.logger.error(
|
|
1082
|
+
{ sessionId: session.id, error: err },
|
|
1083
|
+
"Stall classification callback failed"
|
|
1084
|
+
);
|
|
1085
|
+
session.handleStallClassification(null);
|
|
1086
|
+
});
|
|
1087
|
+
}
|
|
1088
|
+
});
|
|
878
1089
|
}
|
|
879
1090
|
/**
|
|
880
1091
|
* Stop a session
|
|
@@ -1046,6 +1257,22 @@ var PTYManager = class extends import_events2.EventEmitter {
|
|
|
1046
1257
|
return this.sessions.get(sessionId);
|
|
1047
1258
|
}
|
|
1048
1259
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
1260
|
+
// Stall Detection Configuration
|
|
1261
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
1262
|
+
/**
|
|
1263
|
+
* Configure stall detection at runtime.
|
|
1264
|
+
* Affects newly spawned sessions only — existing sessions keep their config.
|
|
1265
|
+
*/
|
|
1266
|
+
configureStallDetection(enabled, timeoutMs, classify) {
|
|
1267
|
+
this._stallDetectionEnabled = enabled;
|
|
1268
|
+
if (timeoutMs !== void 0) {
|
|
1269
|
+
this._stallTimeoutMs = timeoutMs;
|
|
1270
|
+
}
|
|
1271
|
+
if (classify !== void 0) {
|
|
1272
|
+
this._onStallClassify = classify;
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
1049
1276
|
// Runtime Auto-Response Rules API
|
|
1050
1277
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
1051
1278
|
/**
|
|
@@ -1234,6 +1461,14 @@ manager.on("question", (handle, question) => {
|
|
|
1234
1461
|
question
|
|
1235
1462
|
});
|
|
1236
1463
|
});
|
|
1464
|
+
manager.on("stall_detected", (handle, recentOutput, stallDurationMs) => {
|
|
1465
|
+
emit({
|
|
1466
|
+
event: "stall_detected",
|
|
1467
|
+
id: handle.id,
|
|
1468
|
+
recentOutput,
|
|
1469
|
+
stallDurationMs
|
|
1470
|
+
});
|
|
1471
|
+
});
|
|
1237
1472
|
async function handleSpawn(id, config) {
|
|
1238
1473
|
try {
|
|
1239
1474
|
if (manager.has(id)) {
|
|
@@ -1363,6 +1598,8 @@ function deserializeRule(serialized) {
|
|
|
1363
1598
|
pattern: new RegExp(serialized.pattern, serialized.flags || ""),
|
|
1364
1599
|
type: serialized.type,
|
|
1365
1600
|
response: serialized.response,
|
|
1601
|
+
responseType: serialized.responseType,
|
|
1602
|
+
keys: serialized.keys,
|
|
1366
1603
|
description: serialized.description,
|
|
1367
1604
|
safe: serialized.safe
|
|
1368
1605
|
};
|
|
@@ -1373,6 +1610,8 @@ function serializeRule(rule) {
|
|
|
1373
1610
|
flags: rule.pattern.flags || void 0,
|
|
1374
1611
|
type: rule.type,
|
|
1375
1612
|
response: rule.response,
|
|
1613
|
+
responseType: rule.responseType,
|
|
1614
|
+
keys: rule.keys,
|
|
1376
1615
|
description: rule.description,
|
|
1377
1616
|
safe: rule.safe
|
|
1378
1617
|
};
|
|
@@ -1421,6 +1660,35 @@ function handleClearRules(id) {
|
|
|
1421
1660
|
ack("clearRules", id, false, err instanceof Error ? err.message : String(err));
|
|
1422
1661
|
}
|
|
1423
1662
|
}
|
|
1663
|
+
function handleConfigureStallDetection(enabled, timeoutMs) {
|
|
1664
|
+
try {
|
|
1665
|
+
manager.configureStallDetection(enabled, timeoutMs);
|
|
1666
|
+
ack("configureStallDetection", void 0, true);
|
|
1667
|
+
} catch (err) {
|
|
1668
|
+
ack("configureStallDetection", void 0, false, err instanceof Error ? err.message : String(err));
|
|
1669
|
+
}
|
|
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
|
+
}
|
|
1679
|
+
function handleClassifyStallResult(id, classification) {
|
|
1680
|
+
try {
|
|
1681
|
+
const session = manager.getSession(id);
|
|
1682
|
+
if (!session) {
|
|
1683
|
+
ack("classifyStallResult", id, false, `Session ${id} not found`);
|
|
1684
|
+
return;
|
|
1685
|
+
}
|
|
1686
|
+
session.handleStallClassification(classification);
|
|
1687
|
+
ack("classifyStallResult", id, true);
|
|
1688
|
+
} catch (err) {
|
|
1689
|
+
ack("classifyStallResult", id, false, err instanceof Error ? err.message : String(err));
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1424
1692
|
function processCommand(line) {
|
|
1425
1693
|
let command;
|
|
1426
1694
|
try {
|
|
@@ -1520,6 +1788,23 @@ function processCommand(line) {
|
|
|
1520
1788
|
}
|
|
1521
1789
|
handleClearRules(command.id);
|
|
1522
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;
|
|
1798
|
+
case "configureStallDetection":
|
|
1799
|
+
handleConfigureStallDetection(command.enabled ?? false, command.timeoutMs);
|
|
1800
|
+
break;
|
|
1801
|
+
case "classifyStallResult":
|
|
1802
|
+
if (!command.id) {
|
|
1803
|
+
ack("classifyStallResult", command.id, false, "Missing id");
|
|
1804
|
+
return;
|
|
1805
|
+
}
|
|
1806
|
+
handleClassifyStallResult(command.id, command.classification ?? null);
|
|
1807
|
+
break;
|
|
1523
1808
|
default:
|
|
1524
1809
|
emit({ event: "error", message: `Unknown command: ${command.cmd}` });
|
|
1525
1810
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pty-manager",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.15",
|
|
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
|
+
}
|