pty-manager 1.2.16 → 1.2.18

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.mjs CHANGED
@@ -286,6 +286,7 @@ var PTYSession = class extends EventEmitter {
286
286
  messageCounter = 0;
287
287
  logger;
288
288
  sessionRules = [];
289
+ _firedOnceRules = /* @__PURE__ */ new Set();
289
290
  _lastBlockingPromptHash = null;
290
291
  // Stall detection
291
292
  _stallTimer = null;
@@ -293,6 +294,7 @@ var PTYSession = class extends EventEmitter {
293
294
  _stallDetectionEnabled;
294
295
  _lastStallHash = null;
295
296
  _stallStartedAt = null;
297
+ _lastContentHash = null;
296
298
  id;
297
299
  config;
298
300
  get status() {
@@ -379,12 +381,28 @@ var PTYSession = class extends EventEmitter {
379
381
  /**
380
382
  * Start or reset the stall detection timer.
381
383
  * Active when status is "busy" or "authenticating" and stall detection is enabled.
384
+ *
385
+ * Content-based: hashes the ANSI-stripped buffer tail and only resets the
386
+ * timer when visible content actually changes. This prevents TUI spinners
387
+ * (which produce new ANSI sequences but no new visible text) from endlessly
388
+ * resetting the timer.
382
389
  */
383
390
  resetStallTimer() {
384
- this.clearStallTimer();
385
391
  if (!this._stallDetectionEnabled || this._status !== "busy" && this._status !== "authenticating") {
392
+ this.clearStallTimer();
393
+ return;
394
+ }
395
+ const tail = this.outputBuffer.slice(-500);
396
+ const stripped = this.stripAnsiForStall(tail).trim();
397
+ const hash = this.simpleHash(stripped);
398
+ if (hash === this._lastContentHash) {
386
399
  return;
387
400
  }
401
+ this._lastContentHash = hash;
402
+ if (this._stallTimer) {
403
+ clearTimeout(this._stallTimer);
404
+ this._stallTimer = null;
405
+ }
388
406
  this._stallStartedAt = Date.now();
389
407
  this._lastStallHash = null;
390
408
  this._stallTimer = setTimeout(() => {
@@ -400,6 +418,7 @@ var PTYSession = class extends EventEmitter {
400
418
  this._stallTimer = null;
401
419
  }
402
420
  this._stallStartedAt = null;
421
+ this._lastContentHash = null;
403
422
  }
404
423
  /**
405
424
  * Called when the stall timer fires (no output for stallTimeoutMs).
@@ -444,12 +463,19 @@ var PTYSession = class extends EventEmitter {
444
463
  return hash.toString(36);
445
464
  }
446
465
  /**
447
- * Strip ANSI codes for stall detection output.
448
- * Replaces cursor-forward sequences with spaces first.
466
+ * Strip ANSI codes, cursor movement, box-drawing, and spinner characters.
467
+ * Used for stall detection hashing and auto-response pattern matching.
468
+ *
469
+ * Cursor movement codes are replaced with spaces (not removed) to preserve
470
+ * word boundaries — e.g. "Do\x1b[5Cyou" becomes "Do you", not "Doyou".
449
471
  */
450
472
  stripAnsiForStall(str) {
451
- const withSpaces = str.replace(/\x1b\[\d*C/g, " ");
452
- return withSpaces.replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, "");
473
+ let result = str.replace(/\x1b\[\d*[CDABG]/g, " ");
474
+ result = result.replace(/\x1b\[\d*(?:;\d+)?[Hf]/g, " ");
475
+ result = result.replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, "");
476
+ result = result.replace(/[│╭╰╮╯─═╌║╔╗╚╝╠╣╦╩╬┌┐└┘├┤┬┴┼●○❯❮▶◀⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏⣾⣽⣻⢿⡿⣟⣯⣷✻✶✳✢⏺←→↑↓]/g, " ");
477
+ result = result.replace(/ {2,}/g, " ");
478
+ return result;
453
479
  }
454
480
  /**
455
481
  * Handle external stall classification result.
@@ -460,6 +486,7 @@ var PTYSession = class extends EventEmitter {
460
486
  return;
461
487
  }
462
488
  if (!classification || classification.state === "still_working") {
489
+ this._lastContentHash = null;
463
490
  this.resetStallTimer();
464
491
  return;
465
492
  }
@@ -684,10 +711,14 @@ var PTYSession = class extends EventEmitter {
684
711
  if (allRules.length === 0) {
685
712
  return false;
686
713
  }
687
- let stripped = this.stripAnsiForStall(this.outputBuffer);
688
- stripped = stripped.replace(/[│╭╰╮╯─═╌║╔╗╚╝╠╣╦╩╬┌┐└┘├┤┬┴┼●○❯❮▶◀⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏⏺←→↑↓]/g, " ");
689
- stripped = stripped.replace(/ {2,}/g, " ");
714
+ const stripped = this.stripAnsiForStall(this.outputBuffer);
690
715
  for (const rule of allRules) {
716
+ if (rule.once) {
717
+ const ruleKey = `${rule.pattern.source}:${rule.pattern.flags}`;
718
+ if (this._firedOnceRules.has(ruleKey)) {
719
+ continue;
720
+ }
721
+ }
691
722
  if (rule.pattern.test(stripped)) {
692
723
  const safe = rule.safe !== false;
693
724
  const isSessionRule = this.sessionRules.includes(rule);
@@ -711,6 +742,10 @@ var PTYSession = class extends EventEmitter {
711
742
  } else {
712
743
  this.writeRaw(rule.response + "\r");
713
744
  }
745
+ if (rule.once) {
746
+ const ruleKey = `${rule.pattern.source}:${rule.pattern.flags}`;
747
+ this._firedOnceRules.add(ruleKey);
748
+ }
714
749
  this.outputBuffer = this.outputBuffer.replace(rule.pattern, "");
715
750
  const promptInfo = {
716
751
  type: rule.type,
@@ -1883,6 +1918,7 @@ var BunCompatiblePTYManager = class extends EventEmitter3 {
1883
1918
  case "login_required": {
1884
1919
  const session = this.sessions.get(id);
1885
1920
  if (session) {
1921
+ session.status = "authenticating";
1886
1922
  this.emit("login_required", session, event.instructions, event.url);
1887
1923
  }
1888
1924
  break;
@@ -1944,7 +1980,8 @@ var BunCompatiblePTYManager = class extends EventEmitter3 {
1944
1980
  responseType: r.responseType,
1945
1981
  keys: r.keys,
1946
1982
  description: r.description,
1947
- safe: r.safe
1983
+ safe: r.safe,
1984
+ once: r.once
1948
1985
  }));
1949
1986
  this.resolvePending(`getRules:${id}`, rules);
1950
1987
  break;
@@ -2098,7 +2135,8 @@ var BunCompatiblePTYManager = class extends EventEmitter3 {
2098
2135
  responseType: rule.responseType,
2099
2136
  keys: rule.keys,
2100
2137
  description: rule.description,
2101
- safe: rule.safe
2138
+ safe: rule.safe,
2139
+ once: rule.once
2102
2140
  };
2103
2141
  }
2104
2142
  /**