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.d.mts CHANGED
@@ -133,6 +133,8 @@ interface AutoResponseRule {
133
133
  description: string;
134
134
  /** Whether this is safe to auto-respond (default: true) */
135
135
  safe?: boolean;
136
+ /** Fire this rule at most once per session (prevents thrashing on TUI re-renders) */
137
+ once?: boolean;
136
138
  }
137
139
  /**
138
140
  * Blocking prompt info for events
@@ -416,12 +418,14 @@ declare class PTYSession extends EventEmitter {
416
418
  private messageCounter;
417
419
  private logger;
418
420
  private sessionRules;
421
+ private _firedOnceRules;
419
422
  private _lastBlockingPromptHash;
420
423
  private _stallTimer;
421
424
  private _stallTimeoutMs;
422
425
  private _stallDetectionEnabled;
423
426
  private _lastStallHash;
424
427
  private _stallStartedAt;
428
+ private _lastContentHash;
425
429
  readonly id: string;
426
430
  readonly config: SpawnConfig;
427
431
  constructor(adapter: CLIAdapter, config: SpawnConfig, logger?: Logger, stallDetectionEnabled?: boolean, defaultStallTimeoutMs?: number);
@@ -454,6 +458,11 @@ declare class PTYSession extends EventEmitter {
454
458
  /**
455
459
  * Start or reset the stall detection timer.
456
460
  * Active when status is "busy" or "authenticating" and stall detection is enabled.
461
+ *
462
+ * Content-based: hashes the ANSI-stripped buffer tail and only resets the
463
+ * timer when visible content actually changes. This prevents TUI spinners
464
+ * (which produce new ANSI sequences but no new visible text) from endlessly
465
+ * resetting the timer.
457
466
  */
458
467
  private resetStallTimer;
459
468
  /**
@@ -473,8 +482,11 @@ declare class PTYSession extends EventEmitter {
473
482
  */
474
483
  private simpleHash;
475
484
  /**
476
- * Strip ANSI codes for stall detection output.
477
- * Replaces cursor-forward sequences with spaces first.
485
+ * Strip ANSI codes, cursor movement, box-drawing, and spinner characters.
486
+ * Used for stall detection hashing and auto-response pattern matching.
487
+ *
488
+ * Cursor movement codes are replaced with spaces (not removed) to preserve
489
+ * word boundaries — e.g. "Do\x1b[5Cyou" becomes "Do you", not "Doyou".
478
490
  */
479
491
  private stripAnsiForStall;
480
492
  /**
@@ -829,7 +841,7 @@ interface WorkerSessionHandle {
829
841
  id: string;
830
842
  name: string;
831
843
  type: string;
832
- status: 'starting' | 'ready' | 'stopped' | 'error';
844
+ status: SessionStatus;
833
845
  pid: number | undefined;
834
846
  cols: number;
835
847
  rows: number;
package/dist/index.d.ts CHANGED
@@ -133,6 +133,8 @@ interface AutoResponseRule {
133
133
  description: string;
134
134
  /** Whether this is safe to auto-respond (default: true) */
135
135
  safe?: boolean;
136
+ /** Fire this rule at most once per session (prevents thrashing on TUI re-renders) */
137
+ once?: boolean;
136
138
  }
137
139
  /**
138
140
  * Blocking prompt info for events
@@ -416,12 +418,14 @@ declare class PTYSession extends EventEmitter {
416
418
  private messageCounter;
417
419
  private logger;
418
420
  private sessionRules;
421
+ private _firedOnceRules;
419
422
  private _lastBlockingPromptHash;
420
423
  private _stallTimer;
421
424
  private _stallTimeoutMs;
422
425
  private _stallDetectionEnabled;
423
426
  private _lastStallHash;
424
427
  private _stallStartedAt;
428
+ private _lastContentHash;
425
429
  readonly id: string;
426
430
  readonly config: SpawnConfig;
427
431
  constructor(adapter: CLIAdapter, config: SpawnConfig, logger?: Logger, stallDetectionEnabled?: boolean, defaultStallTimeoutMs?: number);
@@ -454,6 +458,11 @@ declare class PTYSession extends EventEmitter {
454
458
  /**
455
459
  * Start or reset the stall detection timer.
456
460
  * Active when status is "busy" or "authenticating" and stall detection is enabled.
461
+ *
462
+ * Content-based: hashes the ANSI-stripped buffer tail and only resets the
463
+ * timer when visible content actually changes. This prevents TUI spinners
464
+ * (which produce new ANSI sequences but no new visible text) from endlessly
465
+ * resetting the timer.
457
466
  */
458
467
  private resetStallTimer;
459
468
  /**
@@ -473,8 +482,11 @@ declare class PTYSession extends EventEmitter {
473
482
  */
474
483
  private simpleHash;
475
484
  /**
476
- * Strip ANSI codes for stall detection output.
477
- * Replaces cursor-forward sequences with spaces first.
485
+ * Strip ANSI codes, cursor movement, box-drawing, and spinner characters.
486
+ * Used for stall detection hashing and auto-response pattern matching.
487
+ *
488
+ * Cursor movement codes are replaced with spaces (not removed) to preserve
489
+ * word boundaries — e.g. "Do\x1b[5Cyou" becomes "Do you", not "Doyou".
478
490
  */
479
491
  private stripAnsiForStall;
480
492
  /**
@@ -829,7 +841,7 @@ interface WorkerSessionHandle {
829
841
  id: string;
830
842
  name: string;
831
843
  type: string;
832
- status: 'starting' | 'ready' | 'stopped' | 'error';
844
+ status: SessionStatus;
833
845
  pid: number | undefined;
834
846
  cols: number;
835
847
  rows: number;
package/dist/index.js CHANGED
@@ -324,6 +324,7 @@ var PTYSession = class extends import_events.EventEmitter {
324
324
  messageCounter = 0;
325
325
  logger;
326
326
  sessionRules = [];
327
+ _firedOnceRules = /* @__PURE__ */ new Set();
327
328
  _lastBlockingPromptHash = null;
328
329
  // Stall detection
329
330
  _stallTimer = null;
@@ -331,6 +332,7 @@ var PTYSession = class extends import_events.EventEmitter {
331
332
  _stallDetectionEnabled;
332
333
  _lastStallHash = null;
333
334
  _stallStartedAt = null;
335
+ _lastContentHash = null;
334
336
  id;
335
337
  config;
336
338
  get status() {
@@ -417,12 +419,28 @@ var PTYSession = class extends import_events.EventEmitter {
417
419
  /**
418
420
  * Start or reset the stall detection timer.
419
421
  * Active when status is "busy" or "authenticating" and stall detection is enabled.
422
+ *
423
+ * Content-based: hashes the ANSI-stripped buffer tail and only resets the
424
+ * timer when visible content actually changes. This prevents TUI spinners
425
+ * (which produce new ANSI sequences but no new visible text) from endlessly
426
+ * resetting the timer.
420
427
  */
421
428
  resetStallTimer() {
422
- this.clearStallTimer();
423
429
  if (!this._stallDetectionEnabled || this._status !== "busy" && this._status !== "authenticating") {
430
+ this.clearStallTimer();
431
+ return;
432
+ }
433
+ const tail = this.outputBuffer.slice(-500);
434
+ const stripped = this.stripAnsiForStall(tail).trim();
435
+ const hash = this.simpleHash(stripped);
436
+ if (hash === this._lastContentHash) {
424
437
  return;
425
438
  }
439
+ this._lastContentHash = hash;
440
+ if (this._stallTimer) {
441
+ clearTimeout(this._stallTimer);
442
+ this._stallTimer = null;
443
+ }
426
444
  this._stallStartedAt = Date.now();
427
445
  this._lastStallHash = null;
428
446
  this._stallTimer = setTimeout(() => {
@@ -438,6 +456,7 @@ var PTYSession = class extends import_events.EventEmitter {
438
456
  this._stallTimer = null;
439
457
  }
440
458
  this._stallStartedAt = null;
459
+ this._lastContentHash = null;
441
460
  }
442
461
  /**
443
462
  * Called when the stall timer fires (no output for stallTimeoutMs).
@@ -482,12 +501,19 @@ var PTYSession = class extends import_events.EventEmitter {
482
501
  return hash.toString(36);
483
502
  }
484
503
  /**
485
- * Strip ANSI codes for stall detection output.
486
- * Replaces cursor-forward sequences with spaces first.
504
+ * Strip ANSI codes, cursor movement, box-drawing, and spinner characters.
505
+ * Used for stall detection hashing and auto-response pattern matching.
506
+ *
507
+ * Cursor movement codes are replaced with spaces (not removed) to preserve
508
+ * word boundaries — e.g. "Do\x1b[5Cyou" becomes "Do you", not "Doyou".
487
509
  */
488
510
  stripAnsiForStall(str) {
489
- const withSpaces = str.replace(/\x1b\[\d*C/g, " ");
490
- return withSpaces.replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, "");
511
+ let result = str.replace(/\x1b\[\d*[CDABG]/g, " ");
512
+ result = result.replace(/\x1b\[\d*(?:;\d+)?[Hf]/g, " ");
513
+ result = result.replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, "");
514
+ result = result.replace(/[│╭╰╮╯─═╌║╔╗╚╝╠╣╦╩╬┌┐└┘├┤┬┴┼●○❯❮▶◀⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏⣾⣽⣻⢿⡿⣟⣯⣷✻✶✳✢⏺←→↑↓]/g, " ");
515
+ result = result.replace(/ {2,}/g, " ");
516
+ return result;
491
517
  }
492
518
  /**
493
519
  * Handle external stall classification result.
@@ -498,6 +524,7 @@ var PTYSession = class extends import_events.EventEmitter {
498
524
  return;
499
525
  }
500
526
  if (!classification || classification.state === "still_working") {
527
+ this._lastContentHash = null;
501
528
  this.resetStallTimer();
502
529
  return;
503
530
  }
@@ -722,10 +749,14 @@ var PTYSession = class extends import_events.EventEmitter {
722
749
  if (allRules.length === 0) {
723
750
  return false;
724
751
  }
725
- let stripped = this.stripAnsiForStall(this.outputBuffer);
726
- stripped = stripped.replace(/[│╭╰╮╯─═╌║╔╗╚╝╠╣╦╩╬┌┐└┘├┤┬┴┼●○❯❮▶◀⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏⏺←→↑↓]/g, " ");
727
- stripped = stripped.replace(/ {2,}/g, " ");
752
+ const stripped = this.stripAnsiForStall(this.outputBuffer);
728
753
  for (const rule of allRules) {
754
+ if (rule.once) {
755
+ const ruleKey = `${rule.pattern.source}:${rule.pattern.flags}`;
756
+ if (this._firedOnceRules.has(ruleKey)) {
757
+ continue;
758
+ }
759
+ }
729
760
  if (rule.pattern.test(stripped)) {
730
761
  const safe = rule.safe !== false;
731
762
  const isSessionRule = this.sessionRules.includes(rule);
@@ -749,6 +780,10 @@ var PTYSession = class extends import_events.EventEmitter {
749
780
  } else {
750
781
  this.writeRaw(rule.response + "\r");
751
782
  }
783
+ if (rule.once) {
784
+ const ruleKey = `${rule.pattern.source}:${rule.pattern.flags}`;
785
+ this._firedOnceRules.add(ruleKey);
786
+ }
752
787
  this.outputBuffer = this.outputBuffer.replace(rule.pattern, "");
753
788
  const promptInfo = {
754
789
  type: rule.type,
@@ -1921,6 +1956,7 @@ var BunCompatiblePTYManager = class extends import_events3.EventEmitter {
1921
1956
  case "login_required": {
1922
1957
  const session = this.sessions.get(id);
1923
1958
  if (session) {
1959
+ session.status = "authenticating";
1924
1960
  this.emit("login_required", session, event.instructions, event.url);
1925
1961
  }
1926
1962
  break;
@@ -1982,7 +2018,8 @@ var BunCompatiblePTYManager = class extends import_events3.EventEmitter {
1982
2018
  responseType: r.responseType,
1983
2019
  keys: r.keys,
1984
2020
  description: r.description,
1985
- safe: r.safe
2021
+ safe: r.safe,
2022
+ once: r.once
1986
2023
  }));
1987
2024
  this.resolvePending(`getRules:${id}`, rules);
1988
2025
  break;
@@ -2136,7 +2173,8 @@ var BunCompatiblePTYManager = class extends import_events3.EventEmitter {
2136
2173
  responseType: rule.responseType,
2137
2174
  keys: rule.keys,
2138
2175
  description: rule.description,
2139
- safe: rule.safe
2176
+ safe: rule.safe,
2177
+ once: rule.once
2140
2178
  };
2141
2179
  }
2142
2180
  /**